c++中 cilkplus openmp for多线程循环效率对比

c++中 cilkplus openmp for多线程循环效率对比

本文为初步测试多线程for不同库之间的效率对比,cilkplus为Intel的一个多线程库(gcc 7.5后移除了该库),OpenMP为使用较多的多线程库,本文测试了在存在数据竞争时的不同线程库的效率,平台为ubuntu。

注意:本测试只是初步测试,数据可能存在误差,编译器使用g++-7.5,编译优化为-O3

1 测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <omp.h>
#include <cilk/cilk.h>

#include "parallel.h"
#include "gettime.h"

using namespace std;

// CAS func (atomic)
template <class ET>
inline bool CAS(ET *ptr, ET oldv, ET newv) {
if (sizeof(ET) == 1) {
return __sync_bool_compare_and_swap((bool*)ptr, *((bool*)&oldv), *((bool*)&newv));
} else if (sizeof(ET) == 4) {
return __sync_bool_compare_and_swap((int*)ptr, *((int*)&oldv), *((int*)&newv));
} else if (sizeof(ET) == 8) {
return __sync_bool_compare_and_swap((long*)ptr, *((long*)&oldv), *((long*)&newv));
}
else {
std::cout << "CAS bad length : " << sizeof(ET) << std::endl;
abort();
}
}

template <class ET>
inline void writeAdd(ET *a, ET b) {
volatile ET newV, oldV;
do {oldV = *a; newV = oldV + b;}
while (!CAS(a, oldV, newV));
}


int main(){
int num[1000];
for(int i=0; i<1000; i++){
num[i] = i;
}
printf("init num list over.\n");

int sum=0;
startTime();
for (int i = 0; i < 1000; ++i)
{
int t = (i * 12) % 1000;
int tmp = num[t];
sum += tmp;
}
nextTime("base time: ");
cout<<sum<<endl;

// openmp reduce
sum = 0;
startTime();
omp_set_num_threads(5);
#pragma omp parallel for reduction(+: sum)
for (int i = 0; i < 1000; ++i)
{
int t = (i * 12) % 1000;
int tmp = num[t];
sum += tmp;
}
nextTime("omp time: ");
cout<<sum<<endl;

// openmp & CAS
sum=0;
startTime();
omp_set_num_threads(5);
#pragma omp parallel for
for (int i = 0; i < 1000; ++i)
{
int t = (i * 12) % 1000;
int tmp = num[t];
writeAdd(&sum, tmp);
}
nextTime("omp & CAS time: ");
cout<<sum<<endl;

// cilk test
sum=0;
setWorkers(5);
startTime();

cilk_for (int i = 0; i < 1000; ++i)
{
int t = (i * 12) % 1000;
int tmp = num[t];
writeAdd(&sum, tmp);
}
nextTime("cilk & CAS time: ");
cout<<sum<<endl;

return 0;
}

测试运行时间结果如下:

1
2
3
4
5
6
7
8
9
init num list over.
base time: : 8.11e-06
498000
omp time: : 0.000205
498000
omp & CAS time: : 8.7e-05
498000
cilk & CAS time: : 0.000253
498000
  • 本测试中直接for循环时间更短是由于本测试线程数只有5,且本测试for循环中代码逻辑较为简单,实际需求开发中for中往往逻辑更加复杂,并行的收益一般都会大于收益。
  • 本实验中,存在数据竞争(多线程同时写sum变量),直接并行会导致结果出错,本实验提供了自实现的CAS操作,同时OpenMP提供reduction操作也可以解决简单的数据竞争。

2 结果总结

  • OpenMP提供的reduction效率不高,但不如使用CAS原子操作
  • OpenMP的效率要略高于cilkplus

c++中 cilkplus openmp for多线程循环效率对比
https://izhuhaoran.github.io/2023/01/23/CPP_Note/for_cilk_openmp效率对比/
作者
zhuhr
发布于
2023年1月23日
许可协议