我想浏览图片并处理与元素的顺序相关的一些特定值。该图像有一个unsigned char*
数组,其中包含一个掩码(如果应该处理像素则为255,否则为0)和一个带有像素值的unsigned short*
数组。
我用tbb实现了三种不同的方法,并通过掩码数组使用单个for循环,并从循环变量x = i%width; y = i/width;
计算x,y坐标。如果像素可见,我想使用Eigen
转换点。
vector4d
是std::vector<std::array<double,4>>
来存储积分。
以下是我对tbb的三个实现:
1。 tbb::combinable
和tbb::parallel_for
:
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;
});
}
2。 tbb::enumerable_thread_specific
和tbb::parallel_for
:
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;
});
}
第3。 tbb::parallel_reduce
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;
}
);
}
我将三个版本的运行时与串行实现进行了比较。阵列有8400000个元素,每个algortihm重复100次。结果是:
我认为combine
语句是这里的瓶颈。我究竟做错了什么?为什么parallel_reduce
太慢?请帮忙!
答案 0 :(得分:0)
您可以在这里应用很少的优化。
const vector4d&
,到处使用[&]
lambdas。vector4d
而不是调整其中一个参数并将其用于return语句。blocked_range2d
代替计算x = i%width; y = i/width
。这不仅可以优化过多的计算,而且更重要的是,它优化了可能提高缓存使用率的缓存访问模式(尽管不是这种情况)。答案 1 :(得分:0)
您正在使用parallel_reduce的功能形式,请尝试更高效的命令式表单。不幸的是,它无法使用lambdas调用,您必须定义一个Body类:
https://www.threadingbuildingblocks.org/docs/help/reference/algorithms/parallel_reduce_func.html
它应该最小化在缩小期间制作的vector4d副本的数量。 vector4d应该是Body类的成员,以便可以重复使用并附加到多个范围,而不是为每个细分范围构造和合并唯一的vector4d。
(注意:拆分构造函数不应复制vector4d成员的内容,请注意上面的intel示例中value
总是初始化为0。)