2016-09-14 79 views
2

全部。爲什麼設置CPU親和力會使線程運行速度變慢?

我寫了一個小案例來測試多線程生產者/消費者模型。我的測試平臺是一款低性能PC(8G內存,J1900四核CPU)。我隔離了Linux內核的核心0,核心1-3沒有使用。生產者線程在覈心1上運行,分配5000000個小對象,將它們放到全局隊列中。消費者線程在覈心2上運行,並從隊列中釋放對象。但是我發現如果我沒有設置它們的CPU親和性(即它們運行在相同的核心0上),那麼時間性能會比設置CPU親和性(8.76s VS 14.66s)好。測試結果保持相似。有人能解釋我的原因嗎?如果我的前提不正確(「設置CPU關聯性可以提高多線程進程的性能」),則至少不應該變得更糟。我的代碼片段如下:

void producer() { 
    Timestamp begin; 

    for (int i = 0; i<data_nb; ++i) { 
    Test* test = new Test(i, i+1); 
    queue.enqueue(test); 
    } 

    Timestamp end; 
    TimeDuration td = end-begin; 
    printf("producer: %ldms(%.6fs)\n", td.asMicroSecond(), td.asSecond()); 
} 

void consumer() { 
    Timestamp begin; 

    do { 
    Test* test = queue.dequeue(); 
    if (test) { 
     nb.add(1); // nb is an atomic counter 
     delete test; 
     test = nullptr; 
    } 
    } while (nb.get() < data_nb); 

    Timestamp end; 
    TimeDuration td = end-begin; 
    //printf("%d data consumed\n", nb.get()); 
    printf("consumer: %ldms(%.6fs)\n", td.asMicroSecond(), td.asSecond()); 
} 
+1

未能設置線程關聯並不能保證線程必須使用核心0. –

+1

請參閱http://stackoverflow.com/questions/39141897/setting-the-affinity-causes-increase-in-execution-time – UmNyobe

+1

人們在理論上通常會認爲設置線程關聯會有所幫助。他們很少檢查。很少有人做檢查會感到驚訝。 – Slava

回答

3

從CPU親和力獲得性能也不如壓入螺紋1至芯1和線2芯2這是一個複雜而沉重的話題研究的那麼簡單,我會觸摸亮點。

首先我們需要定義'性能'。通常,我們對throughput,latency和/或scalability感興趣。三者結合是一個棘手的架構問題,在電信,金融和其他行業受到嚴密審查。

您的情況似乎是由吞吐量度量驅動的。我們希望跨線程的掛鐘時間總和最小。陳述您的問題的另一種方式可能是「影響多線程流程吞吐量的因素是什麼?」

這裏有一些因素很多:

  1. 算法複雜性具有最大的影響力。 Big O,theta,little O在複雜情況下都非常有用。這個例子是微不足道的,但這仍然很重要。表面上,問題是O(n)。根據要分配/釋放元素的數量,時間將是線性的。你的問題是這件事的核心,因爲它表明,物理計算機不能完美地模擬理想的計算機。
  2. CPU資源。如果問題可以並行處理,讓CPU解決問題可以提供幫助。你的問題有一個潛在的假設,即兩個線程會比一個線程好。如果是這樣,也許四個會在兩個之間。再一次,你的實際結果與理論模型相矛盾。
  3. 排隊模型。如果要實現性能增益,理解Queuing模型至關重要。示例問題似乎是經典的單一生產者/單一消費者模型。
  4. 其他資源。根據問題,各種其他資源可能會限制性能。一些因素包括磁盤空間,磁盤吞吐量,磁盤延遲,網絡容量,套接字可用性。這個例子似乎並沒有受到影響。
  5. 內核依賴關係。移至較低級別時,性能可能會受到內核交互量的巨大影響。通常,內核調用需要上下文切換,如果不斷完成,這可能會很昂貴。你的例子可能通過調用new/delete來解決這個問題。
  6. 串行訪問。如果資源需要串行訪問,那麼它將在並行算法中出現瓶頸。你的例子似乎有兩個這樣的問題,新/刪除和入隊/出隊。
  7. CPU緩存。評論提到CPU caching是一種可能性。 L2/L3緩存可能是緩存未命中的來源,也可能是false sharing。我懷疑這是你的例子中的主要問題,但它可能是一個因素。

將這些想法應用到您的示例中,我看到一些問題。我假設你有兩個獨立的線程並行運行。一個線程產生(新)和另一個消耗(刪除)。

堆是串行的。在不同的線程中調用new和delete是已知的性能瓶頸。有幾個小塊並行分配器,包括Hoard

隊列可能是串行的。沒有顯示實現,但入隊/出隊可能是兩個線程之間的序列化點。 lock free ring buffers有很多可以在多個線程之間使用的例子。

線程捱餓。在這個例子中,如果生產者比消費者慢,那麼消費者在很多時候都會空轉。這是構建高性能算法時必須考慮的隊列理論的一部分。有了這些背景知識,我們現在可以得出結論,直到序列化和飢餓問題得到解決,線程親和纔可能不重要。實際上,兩個線程可能會因共享資源互相競爭而慢速運行,或者只是浪費CPU空閒時間。結果,整體吞吐量下降,因此掛鐘時間增加。

工業界對理解這些算法的工程師有着巨大的需求。教育自己很可能是一個有利可圖的事業。

相關問題