最近買了2007.4期《程式員》雜誌,其中有一個多核並發專題,偶在第一時間內翻閱了那些文章,下面摘取了一部分區段觀點。
王昕的《多核計算環境的挑戰--本地代碼的並發》
並發
在電腦領域中,“並發”意味著系統可以在一定的時間段內同時執行多個計算任務的能力,並且在這些計算過程中,不同的計算任務之間還可以共用部分資源。在傳統的定義中,“並發”和另外一個術語--“並行”--之間有著一定的區別("並行"是指:在多個處理器上同時執行相同的任務以便更快地獲得結果)。便為了簡化術語(並且在現在,大部分人也是如此認為的),在本文中,我們將用“並發”這一詞來同時指代這兩者。
並發並不能算是個新鮮的名詞,在作業系統的早期發展過程中(大概是上個世紀60年代),人們就意識到CPU的運算速度和外設的速度之間有著很大的區別,為了節省在當時還算是寶貴的CPU運算時間,讓CPU不會在外設處理資料時等待著,許多多任務處理機制就被設計和開發出來了。
構建並發程式的幾種機制:進程(Process),線程(Thread),纖程(Fiber)。
真正的並發及"偽"並發
根據在同一時刻所啟動並執行計算任務不同,並發可以分為兩種:真正的並發與偽並發。在單一處理器條件下的傳統並發,實質上就是一種偽並發,它是通過作業系統快速地在各個計算任務之間切換從而向使用者展示一種“這些計算任務正在被並發執行”的假象,實際上,在某一確定的時間點上,只有一個線程被運行著。而真正的並發則涉及到多個處理器,由於系統中同時存在著多個處理器,那麼我們就可以讓多個線程在同一時刻同時運行在不同的處理器上。
多處理單元下的並發編程與單處理單元下的並發編程之間的區別
在前面我們提到了“真正的並發”以及“偽並發”,它們兩者之間的區別也自然導致了針對多核(包括多處理器,在下文中統一使用多核代替)的並發編程與針對單核(單一處理器)的並發編程之間存在著一些細微的差別,除了這些“細微”的區別之外,針對單核與多核的並發編程並沒有太大的區別。此處,我們所說的“細微”是指站在應用程式開發人員層面上的視角,實際上在底層的作業系統核心及編譯器最佳化這個層面的編寫複雜度要遠遠大於我們開發應用程式的程式員所想象的。
在多核系統中,有時我們可能會存在著這樣一種需求:將某個線程綁定在某個處理器上執行,這樣就可以減少一些無謂的線程環境切換;而這種需求是在單核系統中無法想像的,因為在單核系統中,所有的線程都是運行在同一個處理器之上的。為了達到我們的需求,我們就必須在程式編寫過程中手動地為該線程設定其處理器的親和性。在windows系統中,我們就可以通過調用SetThreadAffinityMask來達到此類效果。
另外,在多核系統中,有時我們需要在兩個處理器之間進行資料同步,由於涉及到處理器內部的快取同步等因素,我們有時可能並不讓應用程式進入到核心中等待(或者是我們不希望使用傳統的作為核心對象的鎖機制),此時我們就可以使用自旋鎖(spin lock)來保證資料的同步過程不受競爭的影響。和自旋鎖相關的API包括:POSIX中的pthread_spin_init等,以及windows API中的InitializeCriticalSection AndSpinCount等。
前面我們說過,在應用程式層面上來看,單核並發和多核並發之間並無大的區別。實際上,在AMD給出的建議中也是讓程式員盡量去叫用作業系統為我們所提供的API來進行並發程式的編寫。
但是,如果我們切換一下視角,把目光放到底層開發人員上時,我們就將得到另外的答案。由於多核與單核在硬體架構上的不同,所以也自然導致直接和硬體打交道的方法也不同。對於作業系統核心來說,它得修改原來的發送器(scheduler),以保證在多個處理器之間盡量做到Server Load Balancer(load balancing);對於編譯器的最佳化端來說,至少我聽說了,Intel準備在其所出品的ICC編譯器中,將代碼直接編譯成可被並發執行的代碼,這種做法的激進程度甚至超過了另外一個著名的用於開發編程的API:OpenMP。
--------
《Intel的Geoffrey Lowney院士的訪談》
多核平台使得多個程式任務的並存執行更加流暢,同時也能更加高效地運行多線程程式。
對於單個程式來說,只有把程式多線程化才能充分利用多核處理器所提供的硬體並行特性。從這個角度上說,單個軟體是否已經很好地並行化的確會影響到多核處理器是否能充分發揮效能。慶幸的是,很多軟體開發商的程式都已經是多線程化的了……(例如用於改善使用者體驗和程式響應速度的GUI程式),如果一個程式本身是很好地多線程化的,那麼在多核平台上運行應該可以取得比較好的效果。然而由於不同的多核平台可能在微體繫結構上有些差異,針對微體繫結構的效能調優應該可以使程式的效能有進一步的提升(比如編譯器對應用程式進行編譯最佳化)。
《積極準備,謹慎行動--應對多核革命》
在多核時代,我們對程式設計語言的選擇也要更加謹慎。無論開發何種項目,相對於C/C++等編譯型語言往往是通過平台相關的庫來提供多線程支援,C#/JAVA/Python等指令碼語言也許是更好的選擇,原因在於指令碼語言比較進階,一般都提供了對多線程的原生支援。不過PHP/Ruby/Lua等指令碼語言就會比較難得到程式員們的寵愛了--因為它們並沒有提供核心級的線程支援,它們的多線程是使用者級的,甚至不支援線程,用它們編寫的多線程程式仍然無法完全利用多核優勢。
============================
從上面可以看出,多核並發編程,對於我們大多數應用程式的開發人員來說,影響並不是很大的,畢竟在很多程式開發中早已廣泛使用多線程技術了。影響甚大的是作業系統層級及編譯器層級的底層開發,而中國在這個層次開發方面,是較少的,所以在中國近期內影響也不會多麼的嚴重,並不像網路或雜誌製造的那種“狼來了”的感覺。
只是在多核計算時代相對於單核時代的多線程開發,如果資料同步控制不好的程式會更容易被暴露出來。