让我们看一下以下代码:
tbb::blocked_range<int> range(0, a.rows);
uint64_t positive = tbb::parallel_reduce(range, 0, // <- initial value
[&](const tbb::blocked_range<int>& r, uint64_t v)->uint64_t {
for (int y = r.begin(); y < r.end(); ++y) {
auto rA = a[y], rB = b[y];
for (int x = 0; x < a.cols; ++x) {
auto A = rA[x], B = rB[x];
for (int l = y; l < a.rows; ++l) {
auto rAA = a[l], rBB = b[l];
for (int m = x; m < a.cols; ++m) {
if (l == y && m == x)
continue;
auto AA = rAA[m], BB = rBB[m];
if ((A == AA) && (B == BB))
v++; // <- value is changed
if ((A != AA) && (B != BB))
v++; // <- value is changed
}
}
}
}
return v;
}, [](uint64_t first, uint64_t second)->uint64_t {
std::cerr << first << ' + ' << second; // <- wrong values occur
return first+second;
}
);
这是一个并行缩减操作,其中初始值为0.然后,在每个并行计算中,基于初始值,我们计算(第一个lambda函数中的局部变量v
)。第二个lambda函数汇总了并行工作者的结果。
有趣的是,此代码不按预期工作。第二个lambda函数的输出将显示由整数溢出产生的巨大数字。
使用以下代码替换第二行时,代码可以正常工作:
uint64_t positive = tbb::parallel_reduce(range, (uint64_t)0, // <- initial value
现在我想知道。不会对第一个lambda(uint64_t v
)的定义执行此强制转换,以及应该在uint64_t上运行的函数如何在int上运行?
编译器是GCC 6。
答案 0 :(得分:3)
lambda采用什么参数并不重要。根据{{3}},一切都基于第二个参数的类型:
template<typename Range, typename Value,
typename Func, typename Reduction>
Value parallel_reduce( const Range& range, const Value& identity,
const Func& func, const Reduction& reduction,
[, partitioner[, task_group_context& group]] );
伪签名:
Value Func::operator()(const Range& range, const Value& x)
Value Reduction::operator()(const Value& x, const Value& y)
因此,Value
会传递到Func
并传递到Reduction
并返回。如果您希望uint64_t
无处不在,则需要确保Value
为uint64_t
。这就是为什么您的(uint64_t)0
有效,但您的0
没有(并且实际上是未定义的引导行为)。
请注意,这与普通the docs:
相同std::vector<uint64_t> vs{0x7fffffff, 0x7fffffff, 0x7fffffff};
uint64_t sum = std::accumulate(vs.begin(), vs.end(), 0, std::plus<uint64_t>{});
// ^^^ oops, int 0!
// even though I'm using plus<uint64_t>!
assert(sum == 0x17ffffffd); // fails because actually sum is truncated
// and is just 0x7ffffffd