gpt4 book ai didi

c++ - 两个连续的 OpenMP 并行区域相互减慢

转载 作者:太空宇宙 更新时间:2023-11-04 12:43:33 24 4
gpt4 key购买 nike

我希望能够并行运行两个函数并使用 OpenMP 并行区域来解决这个问题。但是这样做我的代码变得更慢了。经过一些测试后,我注意到并行区域不是问题,它确实比以前更快,但我的代码的另一部分(我没有更改)变得慢了 50 倍。

一个小时的测试后,我注意到第二个区域中的 omp parallel for 导致了此行为。似乎两个连续的平行区域相互减慢。我写了一个小测试程序来验证这一点:

#include <iostream>
#include <chrono>
#include <thread>
#include <omp.h>
#include <vector>
#include <cmath>

using namespace std::chrono_literals;

inline void func1()
{
// simulate a cpu function that takes 5ms
std::this_thread::sleep_for(5ms);
}

inline void func2()
{
// simulate a cpu function that takes 6ms
std::this_thread::sleep_for(6ms);
}

int main()
{
// initialize some vectors to test an omp parallel for pragma
std::vector<float> vec1(10000);
std::vector<float> vec2(10000);
std::vector<float> vec3(10000);
for(int i = 0; i < 10000; i++)
{
vec1[i] = (i+1)/1000.0;
vec2[i] = i;
vec3[i] = 0;
}

// timings taken via std::chrono
typedef std::chrono::time_point<std::chrono::high_resolution_clock>
time_point;
typedef std::chrono::duration<double, std::milli> duration;

// first block
std::cout << "serial wait, serial loop" << std::endl;
for(int k = 0; k < 20; k++)
{

time_point start = std::chrono::high_resolution_clock::now();

func1();
func2();

duration time1 = std::chrono::high_resolution_clock::now() - start;
start = std::chrono::high_resolution_clock::now();

for(int i = 0; i < 10000; i++)
{
vec3[i] = sqrt(sin(pow(vec1[i],vec2[i]))*sin(0.5*pow(vec1[i],vec2[i])));
}

duration time2 = std::chrono::high_resolution_clock::now() - start;

std::cout << k << " " << time1.count() << " " << time2.count() << std::endl;
}

// second block
std::cout << "parallel wait, serial loop" << std::endl;
for(int k = 0; k < 20; k++)
{

time_point start = std::chrono::high_resolution_clock::now();

#pragma omp parallel num_threads(2)
{
if(omp_get_thread_num() == 0)
{
func1();
}
else
{
func2();
}
}

duration time1 = std::chrono::high_resolution_clock::now() - start;
start = std::chrono::high_resolution_clock::now();

for(int i = 0; i < 10000; i++)
{
vec3[i] = sqrt(sin(pow(vec1[i],vec2[i]))*sin(0.5*pow(vec1[i],vec2[i])));
}

duration time2 = std::chrono::high_resolution_clock::now() - start;

std::cout << k << " " << time1.count() << " " << time2.count() << std::endl;
}

// third block
std::cout << "serial wait, parallel loop" << std::endl;
for(int k = 0; k < 20; k++)
{

time_point start = std::chrono::high_resolution_clock::now();

func1();
func2();

duration time1 = std::chrono::high_resolution_clock::now() - start;
start = std::chrono::high_resolution_clock::now();

#pragma omp parallel for
for(int i = 0; i < 10000; i++)
{
vec3[i] = sqrt(sin(pow(vec1[i],vec2[i]))*sin(0.5*pow(vec1[i],vec2[i])));
}

duration time2 = std::chrono::high_resolution_clock::now() - start;

std::cout << k << " " << time1.count() << " " << time2.count() << std::endl;
}

// fourth block <-- weird behavior
std::cout << "parallel wait, parallel loop" << std::endl;
for(int k = 0; k < 20; k++)
{

time_point start = std::chrono::high_resolution_clock::now();

#pragma omp parallel num_threads(2)
{
if(omp_get_thread_num() == 0)
{
func1();
}
else
{
func2();
}
}

duration time1 = std::chrono::high_resolution_clock::now() - start;
start = std::chrono::high_resolution_clock::now();

#pragma omp parallel for
for(int i = 0; i < 10000; i++)
{
vec3[i] = sqrt(sin(pow(vec1[i],vec2[i]))*sin(0.5*pow(vec1[i],vec2[i])));
}

duration time2 = std::chrono::high_resolution_clock::now() - start;

std::cout << k << " " << time1.count() << " " << time2.count() << std::endl;
}
}

如果我运行它,我会从控制台获取:

serial wait, serial loop
0 11.8541 3.23881
1 11.4908 3.18409
2 11.8729 3.12847
3 11.6656 3.19606
4 11.8484 3.14534
5 11.863 3.20833
6 11.8331 3.13007
7 11.8351 3.20697
8 11.8337 3.14418
9 11.8361 3.21004
10 11.833 3.12995
11 11.8349 3.14703
12 11.8341 3.1457
13 11.8324 3.14509
14 11.8339 3.12721
15 11.8382 3.14233
16 11.8368 3.14509
17 11.8335 3.14625
18 11.832 3.15115
19 11.8341 3.14499
parallel wait, serial loop
0 6.59906 3.14325
1 6.42459 3.14945
2 6.42381 3.13722
3 6.43271 3.19783
4 6.42408 3.12781
5 6.42404 3.14482
6 6.42534 3.20757
7 6.42392 3.14144
8 6.425 3.14805
9 6.42331 3.1312
10 6.4228 3.14783
11 6.42556 3.15106
12 6.42523 3.14562
13 6.42523 3.14605
14 6.42399 3.12967
15 6.42273 3.14699
16 6.42276 3.15026
17 6.42471 3.14164
18 6.42302 3.14701
19 6.42483 3.19149
serial wait, parallel loop
0 11.8319 4.51681
1 11.4756 0.928738
2 11.1129 0.221045
3 11.1075 0.220827
4 11.1081 0.220197
5 11.1065 0.218774
6 11.1059 0.218329
7 11.1658 0.218804
8 11.1063 0.218056
9 11.107 0.21789
10 11.108 0.218605
11 11.1059 0.217867
12 11.1218 0.218198
13 11.1059 0.217666
14 11.1056 0.219443
15 11.1064 0.217653
16 11.106 0.21729
17 11.1064 0.217565
18 11.1085 0.217965
19 11.1056 0.21735
parallel wait, parallel loop
0 6.41053 6.92563
1 6.06954 4.88433
2 6.4147 0.948097
3 6.41245 5.95226
4 6.41169 4.20988
5 6.41415 3.34145
6 6.41655 4.26902
7 6.41321 1.80355
8 6.41332 1.53747
9 6.41386 1.5394
10 6.06738 1.88866
11 6.41286 1.531
12 6.4133 1.53643
13 6.41356 6.40577
14 6.70144 3.48257
15 6.41551 3.60291
16 6.39516 4.44704
17 6.92893 0.981749
18 6.41533 1.50914
19 6.41685 8.36792

前三个输出 block 正如人们所期望的那样:串行等待 5 和 6 毫秒需要大约 11 毫秒。 vector 计算3.1ms。如果我将我的两个等待并行化,它所花费的时间与两者中最慢的等待时间(6 毫秒)一样多。而(12 线程)并行 for 循环大约需要 0.22 毫秒。

但第四 block ,我连续使用两个平行区域,似乎很奇怪。我的循环时间似乎是随机的,并且比第三个 block 慢得多(即使在最好的情况下)。它反射(reflect)了我的非测试代码中的上述行为。

谁能解释一下,为什么会发生这种情况以及如何解决这个问题?

我编译使用:

g++ main.cpp -O3 -fompenmp

我还在 Windows PC 上测试了这段代码并看到了相同的行为。

如果我删除第一个并行区域中的 num_threads(2),问题不会那么严重,但仍然很明显。 (但是 num_threads(2) 应该只影响第一个区域,不是吗?)

提前致谢。

最佳答案

我在持续重现这个问题时遇到了一些麻烦,但我相信这就是正在发生的事情。

Libgomp(gcc 的 OpenMP 运行时)使用线程池。如果您只有连续的 parallel 循环,则不会产生新的线程。但是,如果您交替使用 parallel for(使用 12 个线程)和 parallel num_threads(2),libgomp 决定将线程池缩小到 2 然后再把它吹到 12。

我通过在并行循环中打印 OpenMP 线程 #1/#2 的 gettid() 验证了这一点。 #1 保持它的 pid,#2 每次迭代都会得到一个新的 pid。正如您自己注意到的那样,您可以轻松解决此问题。正如 Shawn 所指出的,无论如何,parallel sections 是更惯用的解决方案。

至于剩下的差异,我目前无法重现它 - 我得到的结果非常嘈杂。你可以试试pinning the threads to CPUs .否则,如果涉及这么小的时间段,您可能必须通过将并行区域移到调用这两个区域的代码之外来保持并行区域处于事件状态,同时确保正确的控制流。

关于c++ - 两个连续的 OpenMP 并行区域相互减慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52819481/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com