C++ tbb::parallel_reduce vs tbb::combinable vs tbb::enumerable_thread_specific
我想通过一个图像,处理一些与元素的顺序有关的特定值。图像有一个C++ tbb::parallel_reduce vs tbb::combinable vs tbb::enumerable_thread_specific,c++,multithreading,image-processing,tbb,eigen3,C++,Multithreading,Image Processing,Tbb,Eigen3,我想通过一个图像,处理一些与元素的顺序有关的特定值。图像有一个无符号字符*数组,其中包含一个掩码(如果要处理像素,则为255,否则为0)和一个无符号短字符*数组,其中包含像素值 我用tbb实现了三种不同的方法,并通过掩码数组使用了一个for循环,并从循环变量计算了x,y坐标:x=I%width;y=i/宽度。如果像素可见,我想使用Eigen变换点。 vector4d是存储点的std::vector 以下是我对tbb的三个实现: 1tbb::可组合的和tbb::并行的: void Combinab
无符号字符*
数组,其中包含一个掩码(如果要处理像素,则为255,否则为0)和一个无符号短字符*
数组,其中包含像素值
我用tbb实现了三种不同的方法,并通过掩码数组使用了一个for循环,并从循环变量计算了x,y坐标:x=I%width;y=i/宽度代码>。如果像素可见,我想使用Eigen
变换点。
vector4d
是存储点的std::vector
以下是我对tbb的三个实现:
1tbb::可组合的
和
tbb::并行的:
void Combinable(int width, int height, unsigned char* mask,unsigned short* pixel){
MyCombinableType.clear();
MyCombinableType.local().reserve(width*height);
tbb::parallel_for( tbb::blocked_range<int>(0, width*height),
[&](const tbb::blocked_range<int> &r)
{
vector4d& local = MyCombinableType.local();
const size_t end = r.end();
for (int i = r.begin(); i != end; ++i)
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
local.push_back(arr);
}
}
});
vector4d idx = MyCombinableType.combine(
[]( vector4d x, vector4d y)
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
});
}
void Enumerable(int width, int height, unsigned char* mask,unsigned short* pixel){
MyEnumerableType.clear();
MyEnumerableType.local().reserve(width*height);
tbb::parallel_for( tbb::blocked_range<int>(0, width*height),
[&](const tbb::blocked_range<int> &r)
{
enumerableType::reference local = MyEnumerableType.local();
for (int i = r.begin(); i != r.end(); ++i)
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
local.push_back(arr);
}
}
});
vector4d idx = MyEnumerableType.combine(
[](vector4d x, vector4d y)
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
});
}
void Reduce(int width, int height, unsigned char* mask,unsigned short* pixel){
vector4d idx = tbb::parallel_reduce(
tbb::blocked_range<int>(0, width*height ),vector4d(),
[&](const tbb::blocked_range<int>& r, vector4d init)->vector4d
{
const size_t end = r.end();
init.reserve(r.size());
for( int i=r.begin(); i!=end; ++i )
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
init.push_back(arr);
}
}
return init;
},
[]( vector4d x,vector4d y )
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
}
);
}
3<代码>tbb::并行减少:
void Combinable(int width, int height, unsigned char* mask,unsigned short* pixel){
MyCombinableType.clear();
MyCombinableType.local().reserve(width*height);
tbb::parallel_for( tbb::blocked_range<int>(0, width*height),
[&](const tbb::blocked_range<int> &r)
{
vector4d& local = MyCombinableType.local();
const size_t end = r.end();
for (int i = r.begin(); i != end; ++i)
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
local.push_back(arr);
}
}
});
vector4d idx = MyCombinableType.combine(
[]( vector4d x, vector4d y)
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
});
}
void Enumerable(int width, int height, unsigned char* mask,unsigned short* pixel){
MyEnumerableType.clear();
MyEnumerableType.local().reserve(width*height);
tbb::parallel_for( tbb::blocked_range<int>(0, width*height),
[&](const tbb::blocked_range<int> &r)
{
enumerableType::reference local = MyEnumerableType.local();
for (int i = r.begin(); i != r.end(); ++i)
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
local.push_back(arr);
}
}
});
vector4d idx = MyEnumerableType.combine(
[](vector4d x, vector4d y)
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
});
}
void Reduce(int width, int height, unsigned char* mask,unsigned short* pixel){
vector4d idx = tbb::parallel_reduce(
tbb::blocked_range<int>(0, width*height ),vector4d(),
[&](const tbb::blocked_range<int>& r, vector4d init)->vector4d
{
const size_t end = r.end();
init.reserve(r.size());
for( int i=r.begin(); i!=end; ++i )
{
if(mask[i]!=0)
{
array4d arr = {i%width,i/width,(double)pixel[i],1};
//Map with Eigen and transform
init.push_back(arr);
}
}
return init;
},
[]( vector4d x,vector4d y )
{
std::size_t n = x.size();
x.resize(n + y.size());
std::move(y.begin(), y.end(), x.begin() + n);
return x;
}
);
}
void Reduce(整数宽度、整数高度、无符号字符*掩码、无符号短*像素){
vector4d idx=tbb::parallel_reduce(
tbb::分块_范围(0,宽度*高度),矢量4d(),
[&](常量tbb::阻塞的范围&r,向量4D初始化)->vector4d
{
const size_t end=r.end();
初始储备(r.size());
for(int i=r.begin();i!=end;++i)
{
如果(掩码[i]!=0)
{
array4d arr={i%宽度,i/宽度,(双)像素[i],1};
//本征映射与变换
初始推回(arr);
}
}
返回init;
},
[](向量4d x,向量4d y)
{
std::size_t n=x.size();
x、 调整大小(n+y.size());
std::move(y.begin(),y.end(),x.begin()+n);
返回x;
}
);
}
我将三个版本的运行时与串行实现进行了比较。阵列有8400000个元素,每个算法重复100次。结果是:
- 序列号:~170ms
- 可枚举:~118ms
- 可组合:~116ms
- 减少:~720ms
我假设
combine
语句是这里的瓶颈。我做错了什么?为什么parallel\u reduce
soo要慢得多?请帮忙 这里可以应用的优化很少
const vector4d&
,在任何地方使用[&]
lambdasvector4d
,而不是调整其中一个参数的大小,并将其用于return语句blocked_range2d
代替计算x=i%宽度;y=i/宽度
。这不仅优化了过多的计算,而且更重要的是,它优化了可能提高缓存使用率的缓存访问模式(但在本例中不是这样)如果您使用的是parallel_reduce的函数形式,请尝试更有效的命令式形式。很遗憾,不能使用lambdas调用它,必须定义一个Body类: 它应该最大限度地减少在缩减过程中生成的vector4d拷贝数。vector4d应该是Body类的一个成员,这样它就可以被多个范围重用和附加,而不是为每个细分的范围构造和合并一个唯一的vector4d
(注意:拆分构造函数不应复制vector4d成员的内容,请注意,在上面的英特尔示例中,
value
始终初始化为0。)您正在按值将vector4d
传递给lambda表达式。vector4d
的复制构造函数有多贵?我更新了我的问题。我完全忘了提vector4d,谢谢你的提示!请参阅下面我的更好答案,但对代码的一个小改进是:init.reserve(init.size()+r.size())代码>