首先,我认为关于tbb::concurrent_vector
. 此容器类似于std::vector
但具有线程安全的大小调整,但由于内部存储布局,元素访问速度较慢。
你可以在这里阅读更多关于它的信息。
在您的情况下,由于您提出的第二种解决方案(带x * width + y
索引的一维数组),我假设您的算法不涉及密集的多线程数组大小调整。因此,tbb::concurrent_vector
与单线程容器(如std::vector
.
我猜你假设tbb::concurrent_vector
保证线程安全的元素访问,但它没有 - 引用来自doc:tbb::concurrent_vector
::operator[]
获取给定索引处元素的引用。
此方法对于并发读取是线程安全的,并且在增长向量时也是如此,只要调用线程检查了该索引
如果你不调整数组的大小,你只对第一部分感兴趣:线程安全的并发读取。但是 std::vector 甚至原始 C 数组都可以为您提供相同的功能。另一方面,两者都不提供线程安全的任意访问(读/写元素)。
您必须使用锁来实现它,或者找到另一个为您执行此操作的库(可能std::vector
来自 STLPort,但我不确定)。虽然这会非常低效,因为每次访问 2D 数组中的元素都会涉及线程同步开销。虽然我不知道您到底要实现什么,但同步很可能需要比您的实际计算更长的时间。
现在回答您的问题,即使在单线程设置中,使用一维数组表示 ND 数组总是更好,因为计算索引 (x * width + y) 比真正的 ND 数组所需的额外内存访问更快. 对于并发向量,这更是如此,因为在最佳情况下(没有冲突的行访问),您将拥有两倍的锁开销,并且在发生冲突的情况下更多。
因此,在您提出的两种解决方案中,我会毫不犹豫地选择第二种:一维数组(不是必需的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
!)。这样,如果您的访问模式中有一些局部性(一个线程更有可能访问靠近其先前访问的元素而不是远处的元素),那么每个线程都可以处理来自单个图块的大部分数据时间,并且您只有在切换到另一个图块时才会有同步开销。