[英]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;
}
这是我机器上的两次运行:
对于单计算,其中omp atomic
是适用的,预计比跑得更快omp critical
是的,但是,它是可能的,相反,它有时会略微更费时。 例如,它不适用于我上面的第二次运行。 我假设这可能是“应该更快的东西似乎有点慢”的一种情况,当您没有指定线程数(极少数情况)时。 考虑到现代计算机的核心数量很少,因此每次(如提到的 OP)都不太可能获得相同的结果(原子时间 > 临界时间)以获得更多线程数,或者仅使用现代计算机的默认值. (线程数通常由内核数给出,由于超线程,线程数可能是两倍或更多)
事实上,为了让我也复制这样的运行,我试了几次。 (我想展示两种情况都可能的运行,同时使用默认线程数,或不指定线程数)
在我谈论这种行为每次都可能发生的情况之前,我想提一下,我合并第一种情况(没有 openmp 指令)的原因是为了表明它会比使用omp critical
或omp atomic
的情况更快,鉴于我们正在运行的代码段是顺序的。 通过使用critical
/ atomic
,我们只是通过锁定和解锁引入了额外的开销。 (与您在使用 pthread 时比较没有和有互斥锁的情况时所期望的相同)
现在,如果我要复制 OP 面临的情况(原子所用时间的重复行为大于关键),我会将我的线程数设置为尽可能低,同时仍然牢记不要使其成为单线程:(即2)
omp_set_num_threads(2);
现在,可以观察到omp atomic
块所花费的时间总是大于omp critical
块所花费的时间。 例如,使用此修改运行两次:(并非罕见的情况)
为了使原子变体在这两种情况下始终比临界等价物运行得更快,可以使用带有适当参数的减少子句:(即,如果我们正在执行加法,则使用“+”运算符,然后是我们要执行的变量在这里聚集)
#pragma omp parallel for reduction(+:balance)
进行此更改后运行两次(对于两种情况和三种情况),同时将线程数保持为 2:
最后, omp atomic
现在比critical
等价物运行得更快,很像一般预期。
另一组两个运行此更改,但不设置线程数:
omp atomic
不仅性能优于omp critical
,而且它现在(总是)比不使用 omp 指令的 case(第一)运行得更快。 (成功的并行化)
我想增加浮点数与增加整数是不同的。 这取决于CPU体系结构。 当我用整数测试时,可以。
看到我的结果:原子比临界快两倍以上,但是与不使用原子和临界相比,它仍然慢得多,即使结果不正确。
因此,如果可能的话,请尽量避免使用锁,严重的,原子的。
测试结果:
无原子,严重:6666667、0.000113381
原子的:10000000,0.399095
严重:10000000,0.999381
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.