Я хотел иметь возможность запускать две функции параллельно и использовал параллельный регион 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
Первые три блока вывода, как и следовало ожидать:для последовательного ожидания в течение 5 и 6 мс требуется около 11 мс.Расчет вектора 3.1мс.Если я распараллеливаю два ожидания, это занимает столько же времени, сколько и самое медленное из обоих (6 мс).И (12-ниточный) параллельный цикл for занимает около 0,22 мс.
Но четвертый блок, в котором я последовательно использую обе параллельные области, кажется очень странным.Время моего цикла почти случайное и намного медленнее (даже в лучшем случае), чем в третьем блоке.Это отражает вышеупомянутое поведение в моем не тестовом коде.
Может кто-нибудь объяснить мне, почему это происходит и как это решить?
Я компилирую, используя:
g++ main.cpp -O3 -fompenmp
Я также протестировал этот код на ПК с Windows и увидел то же поведение.
Если я удаляю num_threads (2) вВ первом параллельном регионе проблема менее серьезна, но все еще очевидна.(Но num_threads (2) должен затрагивать только первый регион, не так ли?)
Заранее спасибо.