簡體   English   中英

使用 std::thread 時與 g++/OpenMP 相關的錯誤?

[英]Bug related to g++/OpenMP when using std::thread?

我已經將我遇到的問題提煉為它的基本要素。 這是第一個示例代碼:

#include <vector>
#include <math.h>
#include <thread>

std::vector<double> vec(10000);

void run(void) 
{
    for(int l = 0; l < 500000; l++) {

    #pragma omp parallel for
        for(int idx = 0; idx < vec.size(); idx++) {

            vec[idx] += cos(idx);
        }
    }
}

int main(void)
{
    #pragma omp parallel
    {
    }

    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

將其編譯為(在 Ubuntu 20.04 上): g++ -fopenmp main.cpp -o main

編輯:版本:g++(Ubuntu 9.3.0-17ubuntu1~20.04)9.3.0

在 Ryzen 3700x(8 核,16 線程)上運行:運行時間 ~ 43秒,系統監視器中報告的所有 16 個邏輯核心都在 ~ 80%

接下來取出#pragma omp parallel指令,那么main函數就變成了:

int main(void)
{
    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

現在運行時間 ~ 9 秒,系統監視器中報告的所有 16 個邏輯內核都為100%

我還在 Windows 10 上使用 MSVC 編譯了這個,無論#pragma omp 並行指令是否存在,cpu 利用率始終為 ~100%。 是的,我完全知道這條線應該什么都不做,但是使用 g++ 它會導致上述行為; 也只有在線程上調用 run 函數時才會發生,而不是直接調用。 我嘗試了各種編譯標志(-O 級別),但問題仍然存在。 我想下一步是查看匯編代碼,但我看不出這只是 g++ 中的一個錯誤。 任何人都可以對此有所了解嗎? 將不勝感激。

此外,調用 omp_set_num_threads(1); 在循環之前的“void run(void)”函數中,為了檢查單個線程需要多長時間,只有一個線程 100%(如預期)提供了大約70秒的運行時間。

此外,可能相關的問題(雖然這可能是我缺乏理解):調用 omp_set_num_threads(1); 在“int main(void)”函數中(在定義 threaded_call 之前)用 g++ 編譯時什么都不做,即所有 16 個線程仍然在 for 循環中執行,而不管虛假的 #pragma omp 並行指令。 使用 MSVC 編譯時,這只會導致一個線程按預期運行 - 根據 omp_set_num_threads 的文檔,我認為這應該是正確的行為,但對於 g++ 則不然。 為什么不呢,這是另一個錯誤嗎?

編輯:我現在理解的最后一個問題( 從代碼中覆蓋 OMP_NUM_THREADS - 真正的),但仍然使原始問題懸而未決。

感謝 Hristo Iliev 的有用評論,我現在明白了這一點,並想回答我自己的問題,以防萬一它對任何有類似問題的人有用。

問題是,如果在主程序線程中執行任何 OpenMP 代碼,其狀態將變為“污染”——特別是在“#pragma omp parallel”指令之后,OpenMP 線程保持忙碌狀態(全部 16 個),這會影響性能任何 std::thread 線程中的所有其他 OpenMP 代碼,它們產生自己的 OpenMP 線程團隊。 由於主線程僅在程序完成時超出范圍,因此整個程序執行過程中都會存在此性能問題。 因此,如果將 OpenMP 與 std::thread 一起使用,請確保主程序線程中絕對不存在 OpenMP 代碼。

為了證明這一點,請考慮以下修改后的示例代碼:

#include <vector>
#include <math.h>
#include <thread>

std::vector<double> vec(10000);

void run(void) 
{
    for(int l = 0; l < 500000; l++) {

    #pragma omp parallel for
        for(int idx = 0; idx < vec.size(); idx++) {

            vec[idx] += cos(idx);
        }
    }
}

void state(void)
{
#pragma omp parallel
    {
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
}

int main(void)
{
    std::thread state_thread(&state);
    state_thread.detach();

    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

此代碼在前 5 秒內以 80% 的 CPU 使用率運行,然后在程序運行期間以 100% 的 CPU 使用率運行。 這是因為在第一個 std::thread 中產生了一個由 16 個 OpenMP 線程組成​​的團隊並保持忙碌狀態,從而影響了第二個 std::thread 中 OpenMP 代碼的性能。 一旦第一個 std::thread 終止,第二個 std::thread 的性能就不再受到影響,因為第二個 16 個 OpenMP 線程團隊現在不必與第一個競爭 CPU 訪問。 當有問題的代碼在主線程中時,問題一直持續到程序結束。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM