我正在尝试使用OpenMP并行化一个简单的循环。下面是我的代码:
#include <iostream>
#include <omp.h>
#include <time.h>
#define SIZE 10000000
float calculate_time(clock_t start, clock_t end) {
return (float) ((end - start) / (double) CLOCKS_PER_SEC) * 1000;
}
void openmp_test(double * x, double * y, double * res, int threads){
clock_t start, end;
std::cout << std::endl << "OpenMP, " << threads << " threads" << std::endl;
start = clock();
#pragma omp parallel for num_threads(threads)
for(int i = 0; i < SIZE; i++){
res[i] = x[i] * y[i];
}
end = clock();
for(int i = 1; i < SIZE; i++){
res[0] += res[i];
}
std::cout << "time: " << calculate_time(start, end) << std::endl;
std::cout << "result: " << res[0] << std::endl;
}
int main() {
double *dbl_x = new double[SIZE];
double *dbl_y = new double[SIZE];
double *res = new double[SIZE];
for(int i = 0; i < SIZE; i++){
dbl_x[i] = i % 1000;
dbl_y[i] = i % 1000;
}
openmp_test(dbl_x, dbl_y, res, 1);
openmp_test(dbl_x, dbl_y, res, 1);
openmp_test(dbl_x, dbl_y, res, 2);
openmp_test(dbl_x, dbl_y, res, 4);
openmp_test(dbl_x, dbl_y, res, 8);
delete [] dbl_x;
delete [] dbl_y;
delete [] res;
return 0;
}
我如下编译
g++ -O3 -fopenmp main.cpp -o ompTest
但是,在Core-i7上运行测试后,我得到以下结果:
OpenMP,1个线程 时间: 31.468 结果:3.32834e + 12
OpenMP,1个线程 时间: 18.663 结果:3.32834e + 12
OpenMP,2个线程 时间: 34.393 结果:3.32834e + 12
OpenMP,4个线程 时间: 56.31 结果:3.32834e + 12
OpenMP,8个线程 时间: 108.54 结果:3.32834e + 12
我不明白我在做什么错?为什么OpenMP会减慢计算速度?
而且,为什么第一个结果比第二个(两个都有1个omp线程)慢得多?
我的测试环境:Core i7-4702MQ CPU @ 2.20GHz,Ubuntu 18.04.2 LTS,g ++ 7.4.0。
答案 0 :(得分:3)
当前,您创建了线程,但是您却给它们所有相同的工作。
我认为,您忘记了编译指示中的“ for”,这使线程将循环分成多个部分。
#pragma omp parallel for num_threads(threads)
答案 1 :(得分:3)
这里至少有两件事。
clock()
衡量的是处理器的时间,这可以看作是对执行的工作量的度量,而您要衡量的是 wall 的时间>时间。参见OpenMP time and clock() calculates two different results。
并行处理程序中的处理器总时间应比可比较的串行程序中的处理器时间长,因为并行化会增加开销。线程越多,开销越大,因此,增加的线程的速度提高会随着线程的增加而降低,甚至可能变为负数。
与您的代码的这种变化进行比较,该代码实现了一种更合适的方法来测量经过的墙壁时间:
float calculate_time(struct timespec start, struct timespec end) {
long long start_nanos = start.tv_sec * 1000000000LL + start.tv_nsec;
long long end_nanos = end.tv_sec * 1000000000LL + end.tv_nsec;
return (end_nanos - start_nanos) * 1e-6f;
}
void openmp_test(double * x, double * y, double * res, int threads){
struct timespec start, end;
std::cout << std::endl << "OpenMP, " << threads << " threads" << std::endl;
clock_gettime(CLOCK_MONOTONIC, &start);
#pragma omp parallel num_threads(threads)
for(int i = 0; i < SIZE; i++){
res[i] = x[i] * y[i];
}
clock_gettime(CLOCK_MONOTONIC, &end);
for(int i = 1; i < SIZE; i++){
res[0] += res[i];
}
std::cout << "time: " << calculate_time(start, end) << std::endl;
std::cout << "result: " << res[0] << std::endl;
}
对我来说结果是
OpenMP, 1 threads time: 92.5535 result: 3.32834e+12 OpenMP, 2 threads time: 56.128 result: 3.32834e+12 OpenMP, 4 threads time: 59.8112 result: 3.32834e+12 OpenMP, 8 threads time: 78.9066 result: 3.32834e+12
请注意如何将两个线程的测量时间减少一半,但是增加内核数量并不会带来多大改善,最终开始趋向于单线程时间。 * 在我的四核,八超线程计算机上同时执行更多工作的竞争效果,以及与更多线程进行协调相关的开销和资源争用增加。
最重要的是:在任务上抛出更多的线程并不一定会更快地获得结果,并且很少能使您的加速与线程数成正比。
* 全面披露:我从多次运行的结果中挑选出了这些特定的结果。所有人都显示出相似的趋势,但是这一趋势在这一趋势中特别明显-因此可能过分强调了。