Skip to content

(fwData): improve the API of ::fwData::Array

Emilie WERNERT requested to merge 68-improve-the-api-of-fwdata-array into dev

What does this MR do?

Simplifies Array API by moving the methods from the helper to the data itself:

  • deprecate ::fwDataTools::helper::Array
  • integrate the useful methods from the helper to ::fwData::Array
  • deprecate the component attribute
  • add Iterators<Type> and ConstIterator<Type> to iterate through the array buffer

How to test it?

  • Launch fwDataTest to check the new API.
  • Launch all the unit tests and applications to check that the old API is not broken.

Some results

I made some benchmark for the iterators. In Debug, it is 3 times less performant that iterating on the buffer itself because the iterator methods are not properly inlined. In Release, it take the same time.

Tested code

Click to expand

    ::fwData::Array::sptr array = ::fwData::Array::New();

    ::fwData::Array::SizeType size = {512, 512, 512};
    //::fwData::Array::SizeType size = {1000, 1000, 1000};

    array->resize(size, ::fwTools::Type::s_UINT32, true);
    auto lock = array->lock();

    std::fill(array->begin<std::uint32_t>(), array->end<std::uint32_t>(), 0);

    ::fwDataTools::helper::Array helper(array);

    std::uint32_t count = 0;

    double dt1 = 0;
    double dt2 = 0;
    double dt3 = 0;
    double dt4 = 0;

    const size_t NB_ITERATION = 15;
    const size_t NB_ELTS      = array->getNumberOfElements();

    std::vector<std::uint32_t> vect(NB_ELTS, 0);
    std::vector<std::array<double, 4> > results;

    for (size_t i = 0; i < NB_ITERATION; ++i)
    {
        const auto t0 = ::fwCore::HiResClock::getTimeInMilliSec();
        count = 0;

        {
            auto buff          = helper.begin<std::uint32_t>();
            const auto buffEnd = helper.end<std::uint32_t>();

            for(; buff != buffEnd; ++buff)
            {
                *buff = ++count;
            }
        }

        const auto t1 = ::fwCore::HiResClock::getTimeInMilliSec();
        count = 0;

        {
            auto iter         = array->begin<std::uint32_t>();
            const auto endItr = array->end<std::uint32_t>();

            for(; iter != endItr; ++iter)
            {
                *iter = ++count;
            }
        }
        const auto t2 = ::fwCore::HiResClock::getTimeInMilliSec();
        count = 0;

        {
            auto buff = helper.begin<std::uint32_t>();

            for (size_t i = 0; i < NB_ELTS; ++i)
            {
                *buff = ++count;
                ++buff;
            }
        }

        const auto t3 = ::fwCore::HiResClock::getTimeInMilliSec();
        count = 0;

        {
            auto iter = array->begin<std::uint32_t>();

            for (size_t i = 0; i < NB_ELTS; ++i)
            {
                *iter = ++count;
                ++iter;
            }
        }
        const auto t4 = ::fwCore::HiResClock::getTimeInMilliSec();

        const auto d1 = t1-t0;
        const auto d2 = t2-t1;
        const auto d3 = t3-t2;
        const auto d4 = t4-t3;
        dt1 += d1;
        dt2 += d2;
        dt3 += d3;
        dt4 += d4;
        const std::array<double, 4> d = {d1, d2, d3, d4};
        results.push_back(d);
    }

    dt1 /= NB_ITERATION;
    dt2 /= NB_ITERATION;
    dt3 /= NB_ITERATION;
    dt4 /= NB_ITERATION;
    std::stringstream str;
    str <<   "| buffer `for(buff!=buffEnd...)` | iterator `for(it != itEnd...)` | buffer `for(i...)` | iterator for(i ...)` |";
    str << "\n| ------------------------------ | ------------------------------ | ------------------ | -------------------- |";
    for (const auto& res: results)
    {
        str << "\n| " <<
            std::setw(30) << std::setfill(' ') << res[0] << " | " <<
            std::setw(30) << std::setfill(' ') << res[1] << " | " <<
            std::setw(18) << std::setfill(' ') << res[2] << " | " <<
            std::setw(20) << std::setfill(' ') << res[3] << " | ";
    }

    str << "\n| ------------------------------ | ------------------------------ | ------------------ | -------------------- |";

    str << "\n| " <<
        std::setw(30) << std::setfill(' ') << dt1 << " | " <<
        std::setw(30) << std::setfill(' ') << dt2 << " | " <<
        std::setw(18) << std::setfill(' ') << dt3 << " | " <<
        std::setw(20) << std::setfill(' ') << dt4 << " | ";
    OSLM_LOG("total duration:  \n" << str.str());

Sight project to execute the benchmark on your PC

Results

array 512x512x512 without iterator buff != buffEnd iterator it != itEnd array for i... iterator for i ...
Debug (Linux) ~221.267 ms ~626.467 ms ~253.467 ms ~458 ms
Debug (Linux) with asserts ~222.133 ms ~725 ms ~251.8 ms ~608.333 ms
Release (Linux) ~42.5333 ms ~42.0667 ms ~42.2 ms ~45.6667 ms
Debug (macOS) ~287.533 ms ~1039.33 ms ~256.667 ms ~729.467 ms
Debug (macOS) with asserts ~275.4 ms ~1045.73 ms ~241.933 ms ~877.6 ms
Release (macOS) ~64.8 ms ~62.4 ms ~61.6 ms ~66.8 ms
Release (windows-msvc 2017) ~69.133 ms ~258.667 ms ~56.8 ms ~244.133 ms
Debug (windows-msvc 2017) ~315.8 ms ~4864 ms ~301.733 ms ~3645.73 ms
array 1000x1000x1000 without iterator buff != buffEnd iterator it != itEnd array for i... iterator for i ...
Debug (Linux) ~1653.8 ms ~4675.8 ms ~1890.73 ms ~3409 ms
Debug (Linux) with asserts ~1663.87 ms ~5398.33 ms ~1873.2 ms ~4557.6 ms
Release (Linux) ~290.2 ms ~291.4 ms ~291.13 3ms ~320.667 ms

image

image

Associated Issues/Merge Requests

Edited by Julien WAECHTER

Merge request reports