Во-первых, я думаю, что может быть некоторая путаница в отношении 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::vector
;и ThreadSafe_Vector
- это контейнер с потокобезопасным доступом к элементам чтения / записи (не tbb::concurrent_vector
!).Таким образом, если у вас есть некоторая локальность в вашем шаблоне доступа (поток с большей вероятностью получит доступ к элементу рядом с его предыдущим доступом, чем далеко), тогда каждый поток может быть настроен для работы с данными из одной плитки большей частивремя, и у вас есть только накладные расходы синхронизации при переключении на другую плитку.