2016-04-30 42 views
4

Is OpenMP (parallel for) in g++ 4.7 not very efficient? 2.5x at 5x CPU中,我確定我的程序的性能在11s和13s之間變化(大多數情況下總是在12s以上,有時慢到13.4 s)在使用默認的#pragma omp parallel for時約爲500%的CPU,並且在4核8線Xeon上,OpenMP加速僅爲5x CPU w/g++-4.7 -O3 -fopenmp時的2.5倍。OpenMP:不使用超線程核心(half`num_threads()`w/hyperthreading)

我試過使用schedule(static) num_threads(4),並且注意到我的程序總是在11.5s到11.7s(總是低於12s)的時候以320%的CPU完成,例如運行更加一致並且使用更少的資源(即使最佳運行是比超線程少的異常點慢半秒)。

是否有任何簡單的OpenMP方式來檢測超線程,並將num_threads()減少到實際的CPU內核數量?

(有一個類似的問題,Poor performance due to hyper-threading with OpenMP: how to bind threads to cores,但在我的測試中,我發現,從8到4個線程僅僅減少在某種程度上確實是工作W/G ++ - 4.7在Debian 7喘息和Xeon E3-1240v3 ,所以,這個問題僅僅是關於將num_threads()減少到內核數量。)

+3

不,沒有簡單的方法來做到這一點全自動。但有http://stackoverflow.com/q/2901694/620382 +'omp_set_num_threads'。如果可行,我建議再次手動控制每個系統上的線程配置。 – Zulan

+0

這個問題爲什麼downvoted?! – cnst

回答

2

如果你在Linux下運行[假設x86 arch],你可以看看/proc/cpuinfo。有兩個字段cpu coressiblings。第一個是[真正]核心數量,後者是超線程數量。 (例如在我的系統上,我的四核心超線程機器分別是4和8)。

由於Linux可以檢測到[和Zulan的評論中的鏈接],因此該信息也可從x86 cpuid指令獲取。

無論哪種方式,也有這個環境變量:OMP_NUM_THREADS這可能是更容易結合使用與發射器/包裝腳本

一,你不妨考慮的事情是,超過一定數目的線程,可以使存儲器總線飽和,並且不會增加線程[或內核]會提高性能,並且實際上可能會降低性能。

從這個問題:Atomically increment two integers with CAS有來自CppCon 2015年到視頻通話的鏈接分爲兩個部分:https://www.youtube.com/watch?v=lVBvHbJsg5Yhttps://www.youtube.com/watch?v=1obZeHnAwz4

他們每個約1.5小時,但,IMO,非常值得。內存總線/系統在大約四個線程之後趨於飽和。在講話中,講話者(已經完成了大量多線程/多核優化)說,根據他的經驗,內存總線/系統在大約四個線程之後趨於飽和。

0

超線程是英特爾實施的simultaneous multithreading (SMT)。目前的AMD處理器沒有實現SMT(Bulldozer微架構家族有其他的稱之爲基於集羣的多線程的東西,但Zen微架構假設有SMT)。 OpenMP沒有內置的支持來檢測SMT。

如果你想要一個通用的函數來檢測超線程,你需要支持不同代的處理器,並確保處理器是Intel處理器而不是AMD處理器。最好爲此使用一個庫。

但是,您可以使用OpenMP創建功能,該功能適用​​於許多現代Intel處理器,如我所述的here

以下代碼將計算現代英特爾處理器上的物理內核數(它已在我嘗試使用的每個英特爾處理器上工作)。你必須綁定線程才能使其工作。使用GCC,您可以使用 export OMP_PROC_BIND=true,否則您可以使用bind with code(這是我所做的)。

請注意,我不確定這種方法在VirtualBox中是否可靠。將VirtualBox放在一個4核心/ 8邏輯處理器CPU上,並將Windows作爲主機和Linux作爲猜測將虛擬機的核心數量設置爲4,則此代碼報告2個核心,而/ proc/cpuinfo顯示其中兩個核心實際上是邏輯處理器。

#include <stdio.h> 

//cpuid function defined in instrset_detect.cpp by Agner Fog (2014 GNU General Public License) 
//http://www.agner.org/optimize/vectorclass.zip 

// Define interface to cpuid instruction. 
// input: eax = functionnumber, ecx = 0 
// output: eax = output[0], ebx = output[1], ecx = output[2], edx = output[3] 
static inline void cpuid (int output[4], int functionnumber) { 
#if defined (_MSC_VER) || defined (__INTEL_COMPILER)  // Microsoft or Intel compiler, intrin.h included 

    __cpuidex(output, functionnumber, 0);     // intrinsic function for CPUID 

#elif defined(__GNUC__) || defined(__clang__)    // use inline assembly, Gnu/AT&T syntax 

    int a, b, c, d; 
    __asm("cpuid" : "=a"(a),"=b"(b),"=c"(c),"=d"(d) : "a"(functionnumber),"c"(0) :); 
    output[0] = a; 
    output[1] = b; 
    output[2] = c; 
    output[3] = d; 

#else              // unknown platform. try inline assembly with masm/intel syntax 

    __asm { 
    mov eax, functionnumber 
     xor ecx, ecx 
     cpuid; 
    mov esi, output 
     mov [esi], eax 
     mov [esi+4], ebx 
     mov [esi+8], ecx 
     mov [esi+12], edx 
     } 

    #endif 
} 

int getNumCores(void) { 
    //Assuming an Intel processor with CPUID leaf 11 
    int cores = 0; 
    #pragma omp parallel reduction(+:cores) 
    { 
    int regs[4]; 
    cpuid(regs,11); 
    if(!(regs[3]&1)) cores++; 
    } 
    return cores; 
} 

int main(void) { 
    printf("cores %d\n", getNumCores()); 
}