一,、為什么需要多核開發(fā)?
答案很簡單,,目前的芯片制造技術對CPU主頻的提升已經(jīng)達到一個極限了,,也就是說性能的垂直伸縮已經(jīng)不太可能了。因此通過多核的方法,,可以讓程序橫向的伸縮,,這就類似于用多臺服務器實現(xiàn)負載均衡(水平伸縮),而不是簡單的靠將服務器升級成小型機來提供處理能力(垂直伸縮),。
雖然多核并行計算的概念已經(jīng)存在了幾十年了,,但直到最近多核CPU在PC上的普及,多核開發(fā)才不得不提引起程序員的重視,。
多核開發(fā)的本質就是使用多線程進行程序開發(fā),,我們在學數(shù)據(jù)結構和算法的時候,寫的所有的算法都是面向單線程的,。而多核開發(fā)的目的就是將這些算法改造成多線程的支持,,然后系統(tǒng)運行時將這些多線程平均分配到多核處理器上,以實現(xiàn)運行的加速,。
二,、如何進行多核開發(fā)
如果你很熟悉POSIX threads (pthreads) 或者 WinAPI threads,你就可以自己進行開發(fā),。 如果你不想設計過多底層的線程操作,,那就選擇一個并發(fā)開發(fā)平臺,由平臺來自動協(xié)調,,調度和管理多核資源,。并發(fā)開發(fā)平臺包括各種線程池的庫,,例如 .NET的ThreadPool類 Java的Concurrent類 消息傳遞環(huán)境,例如MPI data-parallel編程環(huán)境,,例如NESL, RapidMind, ZPL task-parallel編程環(huán)境, 例如Intel的Threading Building Blocks (TBB) 和 Microsoft的Task Parallel Library (TPL) 動態(tài)編程環(huán)境,例如Cilk or Cilk++或者業(yè)界標準OpenMP. 這些并發(fā)平臺通過提供語言抽象,,擴充注釋或者提供庫函數(shù)的方式來支持多核開發(fā)。
三,、使用并發(fā)開發(fā)平臺具體有哪些好處
我們從下面幾個方面來看:
軟件開發(fā)中最重要的三個考慮的要素就是 程序的性能 (使用多核就是為了提升程序的性能的) 開發(fā)的時間 程序的可靠性
而其中影響開發(fā)時間的三個要素是
伸縮性:如果你自己編寫線程,,你必須考慮用戶是雙核,四核還是八核,。如何將線程自動適應用戶的核數(shù),,并且在多核上將線程均衡的負載。 代碼簡潔:直接使用底層線程庫操作代碼是十分復雜的 模塊化:直接使用底層線程庫操作還會破壞代碼的模塊化
四,、具體實例
下面以Fibonacci的例子來演示:它的遞歸算法經(jīng)常被用來作為多核開發(fā)的例子,。
單核時代,,我們寫Fibonacci代碼的方法如下:
- int fib(int n)
- {
- if (n < 2) return n;
- else {
- int x = fib(n-1);
- int y = fib(n-2);
- return x + y;
- }
- }
-
- int main(int argc, char *argv[])
- {
- int n = atoi(argv[1]);
- int result = fib(n);
- printf("Fibonacci of %d is %d.\n", n, result);
- return 0;
- }
這個算法的核心就是f(n) = f(n-1) + f(n-2),當n很大時,我們希望計算f(n-1)和f(n-2)這兩個任務能否分攤在一個雙核處理器上同時執(zhí)行,。
如果直接使用WinAPI-threaded操作的代碼如下:
- int fib(int n)
- {
- if (n < 2) return n;
- else {
- int x = fib(n-1);
- int y = fib(n-2);
- return x + y;
- }
- }
-
- typedef struct {
- int input;
- int output;
- } thread_args;
-
- void *thread_func ( void *ptr )
- {
- int i = ((thread_args *) ptr)->input;
- ((thread_args *) ptr)->output = fib(i);
- return NULL;
- }
-
- int main(int argc, char *argv[])
- {
- pthread_t thread;
- thread_args args;
- int status;
- int result;
- int thread_result;
- if (argc < 2) return 1;
- int n = atoi(argv[1]);
- if (n < 30) result = fib(n);
- else {
- args.input = n-1;
- status = pthread_create(thread,
- NULL, thread_func,
- (void*) &args );
-
- result = fib(n-2);
-
- pthread_join(thread, NULL);
- result += args.output;
- }
- printf("Fibonacci of %d is %d.\n", n, result);
- return 0;
- }
注意main里面的if(n<30),,當n在30以內時,計算非??欤筒恍枰褂枚嗑€程,當n大于30之后,,我們生成一個線程用來計算f(n-1),而main的主線程將繼續(xù)計算f(n-2),,這樣等兩個線程都結束以后(pthread_join(thread, NULL);),,我們將他們的結果相加。
從這個例子就可以看出,,自己實現(xiàn)線程的缺點:
1 這個例子正好可以用兩個線程分配在兩個核上來實現(xiàn),,可如果一個任務需要16個線程同時執(zhí)行,我們又不知道客戶端到底是幾核的CPU時,,這個任務如何分配就成為一個問題,。
2 這段代碼非常不簡潔
3 額外的結構和函數(shù)也破壞了算法本身的完整性。
下面我們使用多核支持庫來實現(xiàn)該代碼:
使用OpenMP
- int fib(int n) {
- int i, j;
- if (n<2)
- return n;
- else {
- #pragma omp task shared(i)
- i=fib(n-1);
- #pragma omp task shared(j)
- j=fib(n-2);
- #pragma omp taskwait
- return i+j;
- }
-
- }
-
使用Cilk++
- int fib(int n)
- {
- if (n < 2) return n;
- else {
- int x = cilk_spawn fib(n-1);
- int y = fib(n-2);
- cilk_sync;
- return x + y;
- }
- }
-
- int main(int argc, char *argv[])
- {
- int n = atoi(argv[1]);
- int result = fib(n);
- printf("Fibonacci of %d is %d.\n", n, result);
- return 0;
- }
.NET Task Parallel Library中相應的例子
- Private Function FiboFullParallel(ByVal N As Long) As Long
- If N <= 0 Then Return 0
- If N = 1 Then Return 1
-
- Dim t1 As Tasks.Future(Of Long) = Tasks.Future(Of Long).Create( Function() FiboFullParallel(N - 1))
- Dim t2 As Tasks.Future(Of Long) = Tasks.Future(Of Long).Create( Function() FiboFullParallel(N - 2))
-
- Return t1.Value + t2.Value
- End Function
可以看到無論使用哪種并發(fā)平臺,,代碼都非常簡潔,,沒有破壞原有的算法封裝,僅僅通過簡單的改造就可以實現(xiàn)自動任務的分派,。
五,、什么情況下該使用多核編程呢?
如果一個任務的執(zhí)行時間在10-100毫秒,,那么就無需使用多核,,因為將任務通過多線程分解到多核上計算,然后再將結果集合起來的開銷大致需要100毫秒(當然具體多少依據(jù)機器的性能以及你所使用的編譯器的性能),而且還需要消耗內存的空間。
在OpenMP里面我們可以使用"if clause"來給雙核配置增加條件,,例如下面的代碼很明顯,,當n小于100000的時候,不使用多核,,當n大于的時候再使用
- #pragma omp parallel for if(n > 100000)
- for (i = 0; i < n;, i++) {
- ...
- }
六,、后記
本文旨在告訴你為何要進行多核開發(fā),以及簡單展示了多核開發(fā)平臺的使用,。實際的多核開發(fā)要復雜的多,,而且我們知道目前的PC機的多核系統(tǒng)都是基于共享內存的,雖然每個核都有自己的一級緩存,。因此不同核上的線程在運行時就涉及到對資源競爭使用的問題,。除此以外如果應用需要用到IO(硬盤,網(wǎng)絡)的時候,,也存在同樣的問題,。因此多核的設計的難點就在于需要具體情況具體分析,找出多核應用的瓶頸,,通過改進數(shù)據(jù)結構或算法,,消除或優(yōu)化這個瓶頸。
發(fā)表于 @ 2008年11月21日 13:14:00|評論(6
)|收藏
|
評論
還不如用優(yōu)化的串行程序來得快。