我正在尝试使用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。

最佳答案

至少有两件事正在发生。

  • clock()衡量经过的处理器时间,可以将其视为执行的工作量的度量,而您要衡量经过的墙壁时间。参见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;
    }
    

    对我来说结果是



    请注意,两个线程的测量时间如何减少一半,但是增加更多的内核并不能带来多大的改善,最终开始趋向于单线程时间。*这显示出在我的计算机上同时执行更多工作的竞争效果。四核,八超线程计算机,以及与更多线程进行协调相关的开销和资源争用增加。

    底线:在任务上抛出更多线程并不一定会更快地获得结果,并且很少会使您获得与线程数成比例的加速比。

    *全面披露:我从几次运行的结果中挑选出了这些特别的结果。所有人都显示出相似的趋势,但是这一趋势在这一趋势中特别明显-因此可能过分强调了。

    09-16 03:24