2011-01-27 56 views
17

執行摘要: 如何在代碼中指定OpenMP應該只爲線程使用REAL內核,即不計算超線程內核?超線程......讓我的渲染器慢了10倍

詳細分析:多年來,我在空閒時間編寫了一個僅限SW的開源渲染器(rasterizer/raytracer)。 GPL代碼和Windows二進制文件可從這裏獲得: https://www.thanassis.space/renderer.html 它在Windows,Linux,OS/X和BSD下編譯並運行良好。

我上個月介紹了一種光線跟蹤模式 - 生成的圖片質量天空搖滾。不幸的是,光線跟蹤比光柵化要慢幾個數量級。爲了提高速度,就像我爲光柵掃描儀所做的那樣,我將OpenMP(和TBB)支持添加到了光線跟蹤器中 - 以便輕鬆使用更多的CPU內核。光柵化和光線追蹤都很容易進行線程化(按三角形工作 - 每像素工作)。

在家裏,用我的Core2Duo,第二個核心幫助了所有模式 - 光柵化和光線追蹤模式的速度都在1.85x和1.9x之間。

問題:當然,我很好奇,想看看上面的CPU性能(我也「玩」的GPU,preliminary CUDA port),所以我想爲比較堅實的基礎。我將代碼交給了我的一個好朋友,他可以使用一臺16核1500美元的英特爾超級處理器的「野獸」機器。

他經營着它的「最重」模式,光線跟蹤模式...

......他得到五分之一我的酷睿2

喘氣的速度(!) - 恐怖。剛剛發生了什麼?

我們開始嘗試不同的修改,修補程序......並最終找出結果。

通過使用OMP_NUM_THREADS環境變量,可以控制產生了多少OpenMP線程。當線程數量從1增加到8時,速度在增加(接近線性增加)。 當我們越過8時,速度開始減弱,直到它降低到Core2Duo速度的五分之一時,所有16個內核都被使用了!

爲什麼8?

因爲8是real內核的編號。其他8個是......超線程的!

理論:現在,這是我的新聞 - 我已經看到其他算法中的超線程幫助很多(高達25%),所以這是意想不到的。顯然,即使每個超線程核心都帶有自己的寄存器(和SSE單元?),光線跟蹤器也無法利用額外的處理能力。這導致我想...

它可能不是處理能力是餓死 - 它是內存帶寬。

raytracer使用邊界卷層次結構數據結構來加速光線三角形相交。如果使用超線程核心,則一對中的每個「邏輯核心」試圖從該數據結構中的不同位置讀取(即,在內存中) - 並且CPU緩存(本地每對)完全被破壞。至少,這是我的理論 - 任何建議最受歡迎。

因此,問題: OpenMP檢測「核心」的數量併產生線程以匹配它 - 也就是說,它包含計算中的超線程「核心」。就我而言,這顯然會導致災難性的結果,速度明智。有誰知道如何使用OpenMP API(如果可能的話,可攜帶)只爲REAL核心產生線程,而不是超線程的線程?

P.S.代碼是開放的(GPL),可以在上面的鏈接中找到,隨時在自己的機器上重現 - 我猜這會發生在所有超線程CPU上。

P.P.S.請原諒這篇文章的長度,我認爲這是一種教育體驗,並希望分享。

+0

這篇文章有一些有用的答案。 「http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine」 – Dan 2011-01-27 15:09:07

+0

不幸的是,這些並沒有多大幫助 - 它們都報告了一個數字,其中包括超線程「核」 ...... – ttsiodras 2011-01-27 18:33:50

+0

我發現'超線程'可能是很多應用程序的垃圾。在許多情況下,我已經關閉了它(在BIOS中),因爲應用程序不再運行或性能變差。這不僅僅是英特爾(也看到了它的實力)。 – Marm0t 2011-01-27 18:40:56

回答

6

基本上,你需要查詢的環境相當低級別的硬件細節的一些相當簡便的方式 - 和普遍,你不能這樣做,只是從系統調用(操作系統通常不知道即使是硬件線程之間的區別和核心)。

一個支持多個平臺的庫是hwloc - 支持Linux的& windows(和其他),intel &和芯片。 Hwloc會讓你找到關於硬件拓撲的一切,並且知道核心和硬件線程之間的區別(稱爲PU-處理單元 - 以hwloc術語)。所以你可以在開始時調用這個庫,找到實際的內核數量,然後調用omp_set_num_threads()(或者只是在並行部分的開始處將該變量添加爲指令)。

3

不幸的是,您爲什麼會發生這種情況的假設很可能是正確的。可以肯定的是,你將不得不使用一個配置文件工具 - 但我之前用光線跟蹤技術已經看到了這一點,所以這並不奇怪。無論如何,目前還沒有辦法從OpenMP中確定某些處理器是「真實的」,而有些是超線程的。你可以編寫一些代碼來確定,然後自己設置數字。但是,仍然存在OpenMP不會在處理器本身上安排線程的問題 - 它允許操作系統執行此操作。

已經有工作在OpenMP的ARB語委嘗試爲用戶定義一個標準方法來確定他的環境,說怎麼辦好。目前這個討論還在繼續。許多實現允許您通過使用實現定義的環境變量將線程「綁定」到處理器。但是,用戶必須知道處理器的編號以及哪些處理器是「真實的」與超線程的。

1

問題是OMP如何使用HT。 這不是內存帶寬! 我在2.6GHz的HT PIV上試過簡單的循環。 結果是驚人的...

隨着OMP:

$ time ./a.out 
    4500000000 
    real 0m28.360s 
    user 0m52.727s 
    sys 0m0.064s 

沒有OMP: $時間./a.out 45億

real0 m25.417s 
    user 0m25.398s 
    sys 0m0.000s 

代碼:

#include <stdio.h> 
    #define U64 unsigned long long 
    int main() { 
     U64 i; 
     U64 N = 1000000000ULL; 
     U64 k = 0; 
     #pragma omp parallel for reduction(+:k) 
     for (i = 0; i < N; i++) 
     { 
     k += i%10; // last digit 
     } 
     printf ("%llu\n", k); 
     return 0; 
    }