2010-12-13 55 views
1

假設我有這樣效率在多線程

for(i = 0; i < i_max; i++) 
    for(j = 0; j < j_max; j++) 
    // do something 

一個代碼,我想通過使用不同的線程(假設//做一些任務,要做到這一點是相互獨立的,想想比如蒙特卡洛模擬)。我的問題是:爲每個i值創建一個線程會比爲每個j值創建一個線程更好嗎?像這樣的東西

for(i = 0; i < i_max; i++) 
    create_thread(j_max); 

另外:什麼將適當數量的線程?我應該創建i_max線程還是可能在任何給定時間使用同時運行的i_max線程的信號量。

謝謝

+1

這聽起來像你想使用線程池,而不是旋轉大量的線程。 – 2010-12-13 20:05:16

+1

@Anon:線程池只會緩解基本上仍然存在的問題。但是,這可能確實有幫助。 – 2010-12-13 20:08:16

回答

4

分攤工作負載的最佳方式是依賴於工作負載。

廣泛地說 - 對於可並行化的工作負載,使用OpenMP;對於異構工作負載,請使用線程池。如果可以,避免管理自己的線程。

蒙特卡洛模擬應該是真正的並行代碼而不是線程池的好候選。

順便說一句 - 如果您使用的是Visual C++,那麼在Visual C++ v10中就有一個有趣的新的Concurrency Runtime正是這種類型的問題。這有點類似於添加到.Net Framework 4中的任務並行庫,以簡化多核/多CPU代碼的實現。

+0

如果考慮到MSVC附帶的conclib,您可能還會考慮英特爾的TBB。 http://www.threadingbuildingblocks.org/ – 2010-12-13 21:24:49

+0

@John - 是的,這絕對是更好的替代方案。那是多平臺嗎? – 2010-12-13 21:27:36

+0

我不能肯定地說,但我35%肯定答案是'是' – 2010-12-13 22:06:58

1

創建和調用線程的一切都相對昂貴,因此您希望儘可能少地執行該操作。

如果你並行化你的內部循環而不是外部循環,那麼對於外部循環的每次迭代,將創建線程 j_max i_max的順序比使用並行外循環更多。

這就是說,最好的並行化取決於你的實際問題。根據這一點,它可以實際上並行化內部循環。

+0

+1 - 不希望線程管理的成本超過使用並行處理的好處。 – 2010-12-13 20:07:54

0

取決於任務以及您將在何種平臺上進行模擬。例如,在CUDA的體系結構中,您可以分開任務,以便每個i,j,1都單獨完成。

您仍然有時間將數據加載到卡上進行考慮。

使用for循環和類似OpenMP/MPI /自己的線程機制,你基本上可以選擇。在一種情況下,並行線程被分解出來並且j在每個線程上依次循環。在其他情況下,按順序處理循環,並在每次平行化中打開一個循環。

並行(爆發線程)代價高昂。請記住,您需要花費設置n個線程,然後同步n個線程。這表示比例程的運行時間更高的成本c,其本身可以使並行處理的總時間大於單線程模式。這取決於有問題的問題;通常情況下,並行速度越快,其關鍵尺寸就越大。

我建議在第一個for循環中打開並行區域會更快。如果在內部循環中這樣做,則每次循環運行時都必須進行分支/連接,從而爲代碼的速度增加了大量開銷。理想情況下,您只需要創建一次線程即可。

2

避免創建線程,除非您可以讓它們保持忙碌!

如果你的情況是計算綁定,那麼你應該儘量減少你希望你的代碼運行在您產卵於核心數量的線程數。如果創建的線程多於核心,那麼操作系統不得不浪費時間和資源來調度線程以在可用核心上執行。

如果你的情況是IO的限制,那麼你應該考慮使用異步進行排隊,哪些是你從返回異步結果後檢查響應代碼IO操作。同樣,在這種情況下,每IO操作產生一個線程是非常浪費的,因爲您將導致操作系統不得不浪費時間來調度停滯的線程。這裏

+0

+1用於提及工作線程和內核之間的關係。理想情況下,您甚至可以根據實際內核數量使用多個線程,而不是預期的數量。 – SoftMemes 2010-12-13 20:26:35

2

大家基本上是正確的,但這裏的分裂工作,並保持所有的處理器繁忙的一個快速和骯髒的方式。這個工作最好1)創建線程相比,在迭代2所做的工作是昂貴時)最迭代需要大約相同數量的時間來完成

首先,創建每個處理器/核1個線程。這些是你的工作線程。他們閒坐着,直到他們被告知要做點什麼。

現在分手了你的工作,使得工作所需要的同時數據是併攏。我的意思是,如果你在一臺雙處理器機器上處理一個十個元素的數組,你就把它分開,這樣一個組是1,2,3,4,5,另一個是6,7 ,8,9,10。你可能會想把它分成1,3,5,7,9和2,4,6,8,10,但是你會導致更多的錯誤分享(http://en.wikipedia.org/ wiki/False_sharing)。

所以,現在你有每個處理器線程和每個線程的一組數據,你只需告訴每個線程在該數據的一個獨立小組的工作。

所以在你的情況下,我會做這樣的事情。

for (int t=0;t<n_processors;++t) 
{ 
    thread[t]=create_thread(); 
    datamin[t]=t*(i_max/n_processors); 
    datamax[t]=(t+1)*(i_max/n_processors); 
} 

for (int t=0;t<n_processors;++t) 
    do_work(thread[t], datamin[t], datamax[t], j_max) 

//wait for all threads to be done 

//continue with rest of the program. 

當然,我忽略了處理您的數據不是處理器數量的整數倍,而是很容易修復。另外,如果您對第三方庫沒有不利影響,那麼英特爾的TBB(線程構建模塊)可以很好地從您那裏抽象出這些內容,並讓您得到您想要做的實際工作。

+0

快速思考;不能保證所有內核都是相同的,並且兩個內核可能使用相同的資源(例如超線程)。您需要實施某種「偷工作」,以便早日完成線程工作,從而開始在較慢的線程上工作。研究這是一件有趣的事情,但是一個實際編寫線程池的大項目。 ;) – sisve 2010-12-13 21:53:40

+0

是的,整個竊取工作是英特爾的TBB所做的,它不得不自己重新實現。如果你只是在嘗試多線程工作,那麼你可以從少量的工作中獲得80%的性能。 – miked 2010-12-14 00:02:40