繁体   English   中英

OpenMP 关键性能优于原子

[英]OpenMP critical performance better than atomic

我正在尝试来自https://github.com/joeladams/patternlets/blob/master/patternlets/openMP/14.mutualExclusion-critical2/critical2.c的代码来证明 Critical 更耗时,但我不断得到关键的执行时间比 Atomic 快的结果。 有谁知道它是怎么发生的?

// simulate many deposits using atomic
startTime = omp_get_wtime();
#pragma omp parallel for 
for (i = 0; i < REPS; i++) {
    #pragma omp atomic
    balance += 1.0;
}
stopTime = omp_get_wtime();
atomicTime = stopTime - startTime;
print("atomic", REPS, balance, atomicTime, atomicTime/REPS);


// simulate the same number of deposits using critical
balance = 0.0;
startTime = omp_get_wtime();
#pragma omp parallel for 
for (i = 0; i < REPS; i++) {
     #pragma omp critical
     {
         balance += 1.0;
     }
}
stopTime = omp_get_wtime();
criticalTime = stopTime - startTime;
print("critical", REPS, balance, criticalTime, criticalTime/REPS);

我的结果是:

After 1000000 $1 deposits using 'atomic':
        - balance = 1000000.00,
        - total time = 0.421999931335,
        - average time per deposit = 0.000000422000

After 1000000 $1 deposits using 'critical':
        - balance = 0.00,
        - total time = 0.265000104904,
        - average time per deposit = 0.000000265000

谢谢!

发布答案,因为这种情况确实/可以确实存在。 (很大程度上取决于您使用的线程数)

考虑 OP 计算的三种情况(以 10 7 个增量更新balance ,从值 0 开始)到时间并进行比较 - 一种没有任何形式的并行化(或不使用 openmp 指令),一种具有使用omp critical更新balance ,以及使用omp atomic进行更新:

#include <omp.h>
#include <iostream>

int main()
{   const int REPS = 1e+7;
    double balance = 0.0;

    std::cout << "Running without any explicit parallelization from openmp:" << std::endl;
    auto startTime = omp_get_wtime();
    for (int i = 0; i < REPS; i++) {
        balance += 1.0;
    }
    auto stopTime = omp_get_wtime();
    std::cout << "Balance: " << balance << ", Total time taken: " << stopTime - startTime << std::endl;
    // Reset balance:
    balance = 0.0;

    std::cout << "Running with omp critical:" << std::endl;
    startTime = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < REPS; i++) {
        #pragma omp critical
        balance += 1.0;
    }
    stopTime = omp_get_wtime();
    std::cout << "Balance: " << balance << ", Total time taken: " << stopTime - startTime << std::endl;
    // Reset balance:
    balance = 0.0;

    std::cout << "Running with omp atomic:" << std::endl;
    startTime = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < REPS; i++) {
        #pragma omp atomic
        balance += 1.0;
    }
    stopTime = omp_get_wtime();
    std::cout << "Balance: " << balance << ", Total time taken: " << stopTime - startTime << std::endl;
}

这是我机器上的两次运行:

运行(0)

对于单计算,其中omp atomic是适用的,预计比跑得更快omp critical是的,但是,它是可能的,相反,它有时会略微更费时。 例如,它不适用于我上面的第二次运行。 我假设这可能是“应该更快的东西似乎有点慢”的一种情况,您没有指定线程数(极少数情况)时。 考虑到现代计算机的核心数量很少,因此每次(如提到的 OP)都不太可能获得相同的结果(原子时间 > 临界时间)以获得更多线程数,或者仅使用现代计算机的默认值. (线程数通常由内核数给出,由于超线程,线程数可能是两倍或更多)
事实上,为了让我也复制这样的运行,我试了几次。 (我想展示两种情况都可能的运行,同时使用默认线程数,或不指定线程数)

在我谈论这种行为每次都可能发生的情况之前,我想提一下,我合并第一种情况(没有 openmp 指令)的原因是为了表明它会比使用omp criticalomp atomic的情况更快,鉴于我们正在运行的代码段是顺序的。 通过使用critical / atomic ,我们只是通过锁定和解锁引入了额外的开销。 (与您在使用 pthread 时比较没有和有互斥锁的情况时所期望的相同)

现在,如果我要复制 OP 面临的情况(原子所用时间的重复行为大于关键),我会将我的线程数设置为尽可能低,同时仍然牢记不要使其成为单线程:(即2)

omp_set_num_threads(2);

现在,可以观察到omp atomic块所花费的时间总是大于omp critical块所花费的时间。 例如,使用此修改运行两次:(并非罕见的情况)

运行(1)

使固定

为了使原子变体在这两种情况下始终比临界等价物运行得更快,可以使用带有适当参数的减少子句:(即,如果我们正在执行加法,则使用“+”运算符,然后是我们要执行的变量在这里聚集)

#pragma omp parallel for reduction(+:balance)

进行此更改后运行两次(对于两种情况和三种情况),同时将线程数保持为 2:

运行(2)

最后, omp atomic现在比critical等价物运行得更快,很像一般预期。

另一组两个运行此更改,但不设置线程数:

运行(3)

omp atomic不仅性能优于omp critical ,而且它现在(总是)比不使用 omp 指令的 case(第一)运行得更快。 (成功的并行化)

我想增加浮点数与增加整数是不同的。 这取决于CPU体系结构。 当我用整数测试时,可以。

看到我的结果:原子比临界快两倍以上,但是与不使用原子和临界相比,它仍然慢得多,即使结果不正确。

因此,如果可能的话,请尽量避免使用锁,严重的,原子的。

测试结果:

无原子,严重:6666667、0.000113381

原子的:10000000,0.399095

严重:10000000,0.999381

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM