concurrent_vector для 2d массива - PullRequest
       29

concurrent_vector для 2d массива

6 голосов
/ 04 ноября 2011

В настоящее время я пытаюсь представить двумерный массив, используя tbb::concurrent_vector<T>.Этот 2-мерный массив будет доступен для множества различных потоков, поэтому я хочу, чтобы он обрабатывал параллельный доступ максимально эффективно.

Я предложил 2 решения:

  • Используйте tbb::concurrent_vector<tbb::concurrent_vector<T> > для хранения.

  • Сохраните все в tbb::concurrent_vector<T> и получите доступ к элементам с / 1014 *

У меня было предпочтение второго, потому что я не хочу блокировать всю строку для доступа к одному элементу (поскольку я предполагаю, что для доступа к элементу array[x][y] реализация tbb заблокирует x -ю строку, а затем y th элемент).

Я хотел бы знать, какое решение кажется вам более подходящим.

Ответы [ 2 ]

5 голосов
/ 10 ноября 2011

Во-первых, я думаю, что может быть некоторая путаница в отношении tbb::concurrent_vector.Этот контейнер похож на std::vector, но с поточно-ориентированным изменением размера, но более медленным доступом к элементу из-за внутренней структуры хранилища.

Подробнее об этом можно прочитать здесь .

В вашем случае, из-за предложенного вами второго решения (1D-массив с индексированием x * width + y), я предполагаю, что ваш алгоритм не предусматривает интенсивного многопоточного изменения размера массива.Таким образом, вы не выиграете от tbb::concurrent_vector по сравнению с однопоточным контейнером, таким как std::vector.

Полагаю, вы предполагали, что tbb::concurrent_vector гарантирует доступ к потокобезопасным элементам, но это не такfrom tbb::concurrent_vector::operator[] doc:

Получить ссылку на элемент по указанному индексу.

Этот метод является потокобезопасным для одновременных операций чтения, а такжепри увеличении вектора, если вызывающий поток проверил этот индекс

. Если вы не изменяете размер массива, вас интересует только первая часть: параллельное чтение потока.Но std :: vector или даже необработанный массив C дает вам то же самое.С другой стороны, ни один из них не предлагает поточно-ориентированного произвольного доступа (элементы чтения / записи).

Вы должны реализовать это с помощью блокировок или найти другую библиотеку, которая сделает это за вас (возможно, std::vector из STLPortно я не уверен).Хотя это было бы весьма неэффективно, поскольку каждый доступ к элементу из вашего двумерного массива потребует затрат на синхронизацию потоков.Хотя я не знаю, что именно вы пытаетесь реализовать, вполне возможно, что синхронизация займет больше времени, чем ваши фактические вычисления.

Теперь, чтобы ответить на ваш вопрос, даже в однопоточном режиме, это всегдалучше использовать массив 1D для представления массивов ND, поскольку вычисление индекса (x * width + y) выполняется быстрее, чем доступ к дополнительной памяти, необходимый для истинного массива ND.Для параллельных векторов это еще более верно, потому что в лучшем случае вы бы удвоили накладные расходы на блокировку (без конфликтующего доступа к строкам), и намного больше в случае возникновения конфликтов.

Так что изДва предложенных вами решения, я бы не колебался и остановился на втором: массив 1D (не обязательно tbb::concurrent_vector) с адекватной блокировкой для доступа к элементу.

В зависимости от вашего алгоритма и схемы доступа подругие потоки, другой подход - используемый в программном обеспечении для редактирования изображений (gimp, photoshop ...) - основан на плитке:

template<typename T> struct Tile {
    int offsetX, int offsetY;
    int width, height;
    Not_ThreadSafe_Vector<T> data;
};
ThreadSafe_Vector< Tile<T> > data

Где Not_ThreadSafe_Vector может быть любым контейнером без блокировки доступа к элементу, например, std::vectorThreadSafe_Vector - это контейнер с потокобезопасным доступом к элементам чтения / записи (не tbb::concurrent_vector!).Таким образом, если у вас есть некоторая локальность в вашем шаблоне доступа (поток с большей вероятностью получит доступ к элементу рядом с его предыдущим доступом, чем далеко), тогда каждый поток может быть настроен для работы с данными из одной плитки большей частивремя, и у вас есть только накладные расходы синхронизации при переключении на другую плитку.

0 голосов
/ 03 ноября 2018

tbb::concurrent_vector является полностью поточно-ориентированным для доступа к элементам (чтение, запись, получение адреса) и увеличения вектора.Проверьте ответ от сотрудника Intel Arch D. Robison здесь .

Однако операции по очистке или уничтожению вектора не являются поточно-ориентированными (см. PDF-документ по Intel TBB Tutorial $ 6.2.1).То есть, не вызывайте clear(), если на tbb::concurrent_vector.

выполняются другие операции, как уже упоминалось, как говорил Антуан, обработка массива ND как массива 1D всегда предпочтительнее из-за эффективности и производительности..

...