-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TBB may not handle RAII struct very well in parallel_reduce #1251
Comments
To further prove my guess, I tried to use POD struct instead of std container, and manage resource alloc and free manually. The code looks like this: struct res_struct {
double* a = nullptr;
double* b = nullptr;
double* c = nullptr;
bool is_initialized = false;
};
auto res = tbb::parallel_reduce(
tbb::blocked_range{0, ij_end_index * (ij_end_index + 1) / 2, 1024},
res_struct{},
[&](const auto& range, res_struct res) {
if (res.is_initialized == false) {
res.a = (double*)malloc(sizeof(double) * m_size
* m_size * batch_size);
res.b = (double*)malloc(sizeof(double) * m_size
* m_size * batch_size);
res.c = (double*)malloc(sizeof(double) * m_size
* m_size * batch_size);
memset(res.a,
0,
sizeof(double) * m_size * m_size * batch_size);
memset(res.b,
0,
sizeof(double) * m_size * m_size * batch_size);
memset(res.c,
0,
sizeof(double) * m_size * m_size * batch_size);
res.is_initialized = true;
}
// do a lot work about res
return res;
},
[&](res_struct res1, res_struct res2) {
if (res1.is_initialized == false) {
return res2;
}
if (res2.is_initialized == false) {
return res1;
}
for (int m = 0; m < batch_size; m++) {
for (int i = 0; i < m_size * m_size; i++) {
res1.a[m * m_size * m_size + i] +=
res2.a[m * m_size * m_size + i];
res1.b[m * m_size * m_size + i] +=
res2.b[m * m_size * m_size + i];
res1.c[m * m_size * m_size + i] +=
res2.c[m * m_size * m_size + i];
}
}
free(res2.j_matrix_tmp);
free(res2.k_matrix_tmp);
free(res2.k_matrix_b_tmp);
return res1;
}); Now TBB performs better than OpenMP: Please fix it…… |
found this in the document:
However, the |
Hi @LXYan2333, what is the performance with |
Hello, after benchmark several strategies, the I use ITTAPI and vtune to track the whole progress: the yellow task is the parallel Now my question is, is there any parallel way to reduce several large vectors/matrices stored on different threads?
|
BTW, directly use vector as reduction identity is actually an example of the book Pro TBB: on page 170. |
I'm thinking if there is a chance to do reduction on original vector without creating a copy of structure per thread since it introduces a lot of overhead (just working with ranges). |
maybe this should be natively and efficiently supported by TBB, just like OpenMP? |
hello: I wrote a demo: #include <benchmark/benchmark.h>
#include <cstddef>
#include <iostream>
#include <tbb/tbb.h>
#include <valarray>
static constexpr size_t m_size = 8000000;
auto testset() {
std::mt19937 gen(20240228);
std::uniform_real_distribution<double> dist(0, 20.0);
std::valarray<double> vec(m_size);
for (size_t i = 0; i < m_size; ++i) {
vec[i] = dist(gen);
}
return vec;
}
static void tbb_parallel_reduce(benchmark::State& state) {
auto test_set = testset();
for (auto _ : state) {
std::valarray<double> res = tbb::parallel_reduce(
tbb::blocked_range{0uz, m_size, 4},
std::valarray<double>(m_size),
[&](const auto& range, std::valarray<double> res) {
for (size_t i = range.begin(); i < range.end(); ++i) {
res[i] += std::sin(std::pow(std::exp(test_set[i]), 8.0));
}
return res;
},
[&](std::valarray<double> res1, std::valarray<double> res2) {
res1 += res2;
return res1;
});
benchmark::DoNotOptimize(res);
}
}
static void openmp_parallel_reduce(benchmark::State& state) {
auto test_set = testset();
for (auto _ : state) {
std::valarray<double> res(m_size);
#pragma omp parallel
{
std::valarray<double> thread_private_res(m_size);
#pragma omp for schedule(dynamic, 4) nowait
for (size_t i = 0; i < m_size; ++i) {
thread_private_res[i] +=
std::sin(std::pow(std::exp(test_set[i]), 8.0));
}
#pragma omp critical
{ res += thread_private_res; }
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK(openmp_parallel_reduce);
BENCHMARK_MAIN(); It create a valarray on each thread and add something to that valarray. Finally, all valarrays are added into the res valarray. I compile it using clang17 with parameter "-O3 -ffast-math -NDEBUG -march=native" and run this program on my own pc and get the result:
tbb is significantly slower than opemmp. I've checked the result and they all produce the same result. So the conclution is, do not use anything that is expensive to copy as the identity within tbb::parallel_reduce? (I still feel this is a bug……) Thank you pavelkumbrasev for you help and advice. |
Hi @LXYan2333, Unfortunately, for
If you are forced to use previous TBB versions, consider using parallel_reduce interface with the complete Body structure (defining |
Hello, I have an OpenMP parallized program, and I want to migrate to use TBB.
Baically it creates 3 matrices on each threads and do a lot of calculation. At the end, all these matrixs on different threads are added up to 3 final matrices.
So I choose
tbb::parallel_reduce
to parallize it:After migration, the program works as before, but the performance droped. The run time rised from 23.125s to 26.932s.
I did some tests using vtune, and found that memory access is significantly higher than before.
OpenMP (23s)
TBB parallel_reduce (26s)
and in the thread test, there is a lot of spin time:
Spin time of TBB parallel_reduce
I guess it might be caused by TBB copied these matrices (instead of move them) in some cases, so I did a little demo:
run it:
In the ideal case, we need just copy
pig{}
once so 2 starter thread can have their own identitypig{}
. However, the pig is copied 6 times instead. This may lead to observable performance penalty.I guess I can always use TLS to address this issue.
The text was updated successfully, but these errors were encountered: