我目前正在尝试使用tbb::concurrent_vector<T>
来表示2D数组。这个2d数组将被许多不同的线程访问,这就是为什么我希望它能够最有效地处理并行访问。
我想出了两个解决方案:
使用tbb::concurrent_vector<tbb::concurrent_vector<T> >
进行存储。
将所有内容存储在tbb::concurrent_vector<T>
和访问元素w / x * width + y
我偏爱第二个,因为我不想锁定整行来访问一个元素(因为我假设要访问元素array[x][y]
,tbb实现将锁定x
第一行,然后是y
元素。)
我想知道哪种解决方案对您更好。
答案 0 :(得分:5)
首先,我认为tbb::concurrent_vector
可能存在一些混淆。此容器与std::vector
类似,但由于内部存储布局,可以通过线程安全调整大小但访问速度较慢。
您可以详细了解here。
在您的情况下,由于您提出的第二个解决方案(带有x * width + y
索引的1D数组),我假设您的算法不涉及数组的密集多线程大小调整。因此,与tbb::concurrent_vector
等单个线程容器相比,您不会从std::vector
中受益。
我猜你假设tbb::concurrent_vector
保证了线程安全的元素访问权限,但它没有 - 引自tbb::concurrent_vector
::operator[]
doc:
获取给定索引处元素的引用。
此方法对于并发读取是线程安全的,并且在生成向量时也是如此,只要调用线程检查了该索引
如果你没有调整数组的大小,你只对第一部分感兴趣:线程安全的并发读取。但是std :: vector甚至原始C数组给你的相同。另一方面,它们都不提供线程安全的任意访问(读/写元素)。
您必须使用锁实现它,或者找到另一个为您执行此操作的库(可能是STLPort中的std::vector
但我不确定)。虽然这样效率很低,但是因为每次从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
!)。
这样,如果您的访问模式中有一些位置(一个线程更可能访问其先前访问的元素而不是远处),那么可以使每个线程处理来自单个区块的数据。时间,切换到另一个磁贴时只有同步开销。
答案 1 :(得分:-1)
tbb::concurrent_vector
是完全线程安全的,用于访问元素(读,写,获取地址)和扩展向量。检查英特尔员工Arch D. Robison here的回复。
但是,清除或销毁向量的操作不是线程安全的(请参阅$ 6.2.1英特尔TBB教程pdf文档)。也就是说,如果clear()
上正在进行其他操作,请不要调用tbb::concurrent_vector
。
正如Antoine所述,由于效率和性能,始终首选将ND阵列处理为一维阵列。