tbb::parallel_reduce vs tbb::combinable vs tbb::enumerable_t
tbb::parallel_reduce vs tbb::combinable vs tbb::enumerable_thread_specific
我想浏览一张图像,并处理一些关于元素顺序的特定值。图像有一个包含掩码的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个元素,每个算法重复100次。结果如下:
- 系列:~ 170 ms
- 可列举的:~ 118 ms
- 可以化合的:~ 116 ms
- 减少:~ 720 ms
我假设combine
语句是这里的瓶颈。我做错了什么?为什么parallel_reduce
这么慢?请帮助!
您可以在这里应用一些优化。
- 避免过度复制:传递
const vector4d&
代替,使用[&]
lambda无处不在 - 在堆栈上使用临时
vector4d
,而不是调整其中一个参数的大小,并将其用于返回语句。 - 一般使用
blocked_range2d
,不计算x = i%width; y = i/width
。这不仅优化了过多的计算,而且更重要的是,它优化了缓存访问模式,这可能会提高缓存使用率(尽管不是在这种情况下)。
如果您正在使用parallel_reduce的函数形式,请尝试更有效的命令式形式。不幸的是,它不能使用lambdas调用,您必须定义Body类:
https://www.threadingbuildingblocks.org/docs/help/reference/algorithms/parallel_reduce_func.html它应该最小化在您的还原过程中生成的vector4d副本的数量。vector4d应该是你的Body类的成员,这样它就可以被重用和附加到多个范围,而不是为每个细分的范围构建和合并一个唯一的vector4d。
(注意:分割构造函数不应该复制vector4d成员的内容,注意上面intel的例子中value
总是初始化为0)
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 数据成员SFINAE的C++17测试:gcc vs clang
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 正在VS调试器中监视映射条目
- Confusion: decltype vs std::function
- 将IBM Rhapsody模型集成到VS 2019中
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用VS Code和CMake Tools运行自定义命令
- 修改 VS Code 中的默认C++代码段
- 如何使用c++在VS 2019上运行SQL查询
- vs 2015 constexpr变量不恒定,但与2019相比还好吗
- 完美前进使用 std::forward vs RefRefCast
- 从VS 2015更新3更新到VS2015更新3 d后浮点计算行为不同的原因
- VS 2015 链接错误 无法构建依赖于 libcurl 的项目
- consteval wrapper vs. source_location
- TBB vs.本地工作队列
- tbb::parallel_reduce vs tbb::combinable vs tbb::enumerable_t
- 微软TPL(任务并行库)vs英特尔TBB(线程构建块)