簡體   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