首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenMP并行循环比规则循环慢得多

OpenMP并行循环比规则循环慢得多
EN

Stack Overflow用户
提问于 2022-04-06 13:21:36
回答 1查看 272关注 0票数 2

整个程序已缩减为一个简单的测试:

代码语言:javascript
复制
    const int loops = 1e10;
    int j[4] = { 1, 2, 3, 4 };
    time_t time = std::time(nullptr);
    for (int i = 0; i < loops; i++) j[i % 4] += 2;
    std::cout << std::time(nullptr) - time << std::endl;

    int k[4] = { 1, 2, 3, 4 };
    omp_set_num_threads(4);
    time = std::time(nullptr);
#pragma omp parallel for
    for (int i = 0; i < loops; i++) k[omp_get_thread_num()] += 2;
    std::cout << std::time(nullptr) - time << std::endl;

在第一种情况下,运行循环大约需要3秒,在第二种情况下,结果是不一致的,可能是4-9秒。这两个循环运行得更快,并且启用了一些优化(比如支持速度和整个程序优化),但是第二个循环仍然显着地慢。我尝试在循环结束时添加屏障,并显式地将数组指定为shared,但这没有帮助。当我设法使并行循环运行得更快时,唯一的情况是使循环变为空。可能出了什么问题?

Windows 10 x64,CPU Intel Core i5 10300 H (4核)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-07 07:12:32

正如在各种评论中已经指出的,问题的症结是虚假共享。事实上,你的例子就是一个人可以实验的典型例子。但是,您的代码中也有很多问题,例如:

  • 您可能会在loops变量以及所有jk表中看到溢出;
  • 你的计时器并不是最好的选择(诚然,这是我在这个例子中有点迂腐);
  • 您不使用您计算的值,这允许编译器完全忽略各种计算;
  • 您的两个循环不是等价的,也不会给出相同的结果;为了使其正确,我回到了您最初的i%4公式,并添加了一个schedule( static, 1)子句。这不是一种正确的方法,但是它只是在没有使用正确的reduction子句的情况下得到预期的结果。

然后,我重写了您的示例,并添加了我认为更好的解决错误共享问题的方法:使用reduction子句。

代码语言:javascript
复制
#include <iostream>
#include <omp.h>

int main() {
    const long long loops = 1e10;
    long long j[4] = { 1, 2, 3, 4 };
    double time = omp_get_wtime();
    for ( long long i = 0; i < loops; i++ ) {
         j[i % 4] += 2;
    }
    std::cout << "sequential: " << omp_get_wtime() - time << std::endl;

    time = omp_get_wtime();
    long long k[4] = { 1, 2, 3, 4 };
    #pragma omp parallel for num_threads( 4 ) schedule( static, 1 )
    for ( long long i = 0; i < loops; i++ ) {
        k[i%4] += 2;
    }
    std::cout << "false sharing: " << omp_get_wtime() - time << std::endl;

    time = omp_get_wtime();
    long long l[4] = { 1, 2, 3, 4 };
    #pragma omp parallel for num_threads( 4 ) reduction( +: l[0:4] )
    for ( long long i = 0; i < loops; i++ ) {
        l[i%4] += 2;
    }
    std::cout << "reduction: " << omp_get_wtime() - time << std::endl;

    bool a = j[0]==k[0] && j[1]==k[1] && j[2]==k[2] && j[3]==k[3];
    bool b = j[0]==l[0] && j[1]==l[1] && j[2]==l[2] && j[3]==l[3];
    std::cout << "sanity check: " << a << " " << b << std::endl;

    return 0;
}

在我的笔记本上编译和运行而不进行优化:

代码语言:javascript
复制
$ g++ -O0 -fopenmp false.cc 
$ ./a.out 
sequential: 15.5384
false sharing: 47.1417
reduction: 4.7565
sanity check: 1 1

这说明了reduction条款所带来的改进。现在,从编译器中启用优化提供了一个更缓和的画面:

代码语言:javascript
复制
$ g++ -O3 -fopenmp false.cc 
$ ./a.out 
sequential: 4.8414
false sharing: 4.10714
reduction: 2.10953
sanity check: 1 1

如果说有什么区别的话,这表明编译器现在很擅长于避免大多数错误的共享。实际上,对于初始(错误的) k[omp_get_thread_num()],使用和不使用reduction子句都没有时间差,这表明编译器能够避免这个问题。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71767545

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档