在過去一段長時間里,x86系統(tǒng)上軟件的性能隨著來自Intel和AMD處理器速度越來越快而不斷提高,開發(fā)者只需對現(xiàn)有軟件程序作輕微改動就能坐觀其性能在隨著硬件性能的上升而不斷提升。不過,多核設計概念的出現(xiàn)迫使軟件世界不得不直面并行性(將單個任務拆分成多個小塊以便分別處理之后再重新組合的能力)問題。當然,為服務器設計軟件的開發(fā)者已經(jīng)解決了一些此類難題,因為多核處理器和多路系統(tǒng)在服務器市場已經(jīng)存在多年(在傳統(tǒng)的Unix領域),一些運行在RISC架構(gòu)多核多路系統(tǒng)上的應用程序已經(jīng)被設計成多線程以利用系統(tǒng)的并行處理能力。但是,在x86領域,應用程序開發(fā)者多年來一直停留在單線程世界,生產(chǎn)所謂的“順序軟件”。
  
    現(xiàn)在的情況是軟件開發(fā)者必須找出新的開發(fā)軟件的方法,面向?qū)ο缶幊痰呐d起增加了匯編語言的復雜性,并行編程也需要新的抽象層次。另一方面,處理器設計廠商在設計產(chǎn)品時也應該將軟件開發(fā)者考慮在內(nèi),“處理器的首要著眼點應該是可編程性,而不是速度?!盨utter說。多核處理器要想發(fā)揮出威力,關(guān)鍵在于并行化軟件支持,多核設計帶動并行化計算的推進,而給軟件帶來的影響更是革命性的。
  
    Intel很早就通過超線程技術(shù)實現(xiàn)了邏輯上的雙處理器系統(tǒng),可以并行計算,但這不過是對處理器閑置資源的一種充分利用而已,并且這種充分利用只有在特定的條件下,尤其是針對流水線比較長且兩種運算并不相互交叉的時候,才會有較高的效率,如編碼解碼、長期重復某種矩陣運算以及一些沒有經(jīng)過仔細編寫的軟件等。
  
    即使IBM的Power5架構(gòu),也需要跟最新的操作系統(tǒng)進行融合,加上運行在其上的軟件,才有可能利用并發(fā)多線程。虛擬化技術(shù)在一定程度上能夠處理一些因為多核帶來的問題,可以讓應用軟件和操作系統(tǒng)在透明的環(huán)境下對處理器資源進行分配和管理。
  
    目前在對稱多處理器方面,操作系統(tǒng)對資源的分配和管理并沒有本質(zhì)的改變,多以對稱的方式進行平均分配。也就是說,在操作系統(tǒng)層面,當一個任務到來時,剝離成為兩個并行的線程,因為線程之間需要交流以及操作系統(tǒng)監(jiān)管,它導致的效率損失要比硬件層面大得多。并且,多數(shù)軟件并沒有充分考慮到雙核乃至多核的運行情況,導致線程的平均分配時間以及線程之間的溝通時間都會大大增加,尤其是當線程需要反復訪問內(nèi)存的時候。目前,多數(shù)操作系統(tǒng)還沒有完全實現(xiàn)自由的資源分配,如IBM是通過AIX 5.3L來支持Power5上的虛擬化功能,才實現(xiàn)了資源的動態(tài)調(diào)配和劃分的。
  
    從長遠來看,需要使用虛擬化技術(shù)才可能實現(xiàn)操作系統(tǒng)對任務的具體劃分,這很可能改變一些通用的編程模式。
  
    面對多核系統(tǒng),需要有并行編程的思想才有可能充分利用資源,而人類的思維模型習慣于線性思維,對“面”或者更為復雜的立體編程模式,效率會下降很多。軟件在并行化方面的滯后給多核處理器技術(shù)的發(fā)展蒙上了一些陰影。盡管用戶在充滿希望地期待著,但思維的改變不是一朝一夕的事情。服務器端的并行化應用設計似乎已經(jīng)解決了一些,但仍需要努力提高其擴展能力。
  
    并行化對于客戶端的應用是一項巨大的挑戰(zhàn),而對于許多基于服務器的程序來說,卻是一個“已經(jīng)圓滿解決的問題”。我們在服務器上使用的并行化架構(gòu)一直在很好地工作,但要想確保這類架構(gòu)的順利編程和擴展能力,人們?nèi)匀恍枰冻鼍薮蟮呐?。這些應用通常都具有很強的并行特性,它們可以同時處理許多獨立的請求流。例如,一臺Web服務器或一個Web站點可以獨立執(zhí)行同一代碼在大部分不重疊數(shù)據(jù)上的數(shù)千個版本。此外,這些執(zhí)行都是相互獨立的,通過一個抽象數(shù)據(jù)存儲來共享狀態(tài),例如通過支持高度并行化訪問結(jié)構(gòu)化數(shù)據(jù)庫。今天,表達并行的方式有很多種,每種方式只適用于一個特定的程序子集。這些并行編程模型在兩方面的區(qū)別比較大:并行運行的粒度和任務間相互配合的程度。
  
    并行執(zhí)行的操作可能是單個指令,如加法或乘法,也可能是需要用幾天時間來運行的復雜程序。很明顯,對于小型操作來說,并行基礎設施的開銷成本是非常巨大的,總的來說,任務分割得越小,將其生成為單個任務和提供通信及同步時所需要的成本就越高。
  
    另外一個方面就是操作間通信和同步配合的程度。最理想的程序是沒有任何的配合,操作完全是獨立運行的,而且產(chǎn)生的結(jié)果也是完全不同的。在這種情況下,操作可以按任何順序運行,也不會產(chǎn)生任何同步或通信成本,在編程時無須考慮數(shù)據(jù)競爭的問題,編程過程自然也就會非常容易。然而,這種狀態(tài)是非常罕見的,多數(shù)并行程序都要在操作之間共享數(shù)據(jù)。當操作變得越來越多樣化時,確保正確和有效的操作其復雜性也會隨之提高。最簡單的案例是為每個操作執(zhí)行相同的代碼,這種共享類型是一種無規(guī)律的并行方式。更具有挑戰(zhàn)性的是,無規(guī)劃的并行方式,在這種方式中,操作是完全不同的,而且共享方式也更難以理解。
  
    獨立并行 在這種方式下,一種或多種操作是針對數(shù)據(jù)集中的每個項目獨立實施執(zhí)行的。精細的數(shù)據(jù)并行依靠的是操作的并行執(zhí)行,它們不應當共享輸入的數(shù)據(jù)或結(jié)果,并應當在無協(xié)調(diào)的情況下完全可以執(zhí)行。
  
    在實際應用中,搜索引擎等應用只共享一個大型的只讀數(shù)據(jù)庫,因此并發(fā)的處理檢索不需要任何協(xié)調(diào),同樣,大型模擬通常需要在運行時分析大量的輸入?yún)?shù),也是一種獨立并行模型。
  
    有規(guī)律的并行比獨立并行更復雜一些的是當運算之間存在相互依存關(guān)系時,將同一種運算應用到一個數(shù)據(jù)集上。如果兩個運算之間存在通信或同步,則在數(shù)據(jù)上某一部分執(zhí)行的運算會與另外一個運算存在依存關(guān)系。有規(guī)律的并行程序要想取得正確的結(jié)果,需要同步或認真協(xié)調(diào)執(zhí)行策略,與一般并行不同的是,我們可以對這類操作背后的代碼進行分析,確定如何以并發(fā)的方式對其加以執(zhí)行,以及確定它們共享哪些數(shù)據(jù)。
  
    無結(jié)構(gòu)的并行acerun: yes”> 無結(jié)構(gòu)的并行對數(shù)據(jù)的訪問是不可預見的,而且需要通過明確的同步方式對其加以協(xié)調(diào)。在使用線程和明確的同步方式寫成的程序中,這種并行形式最為普遍。在這類程序中每一個線程都有自己特定的職責,我們很難討論這類并行形式中任何具體的內(nèi)容,但有一點:兩個線程在訪問數(shù)據(jù)時如果發(fā)生沖突,就必須使用明確的同步方式來解決,否則,程序?qū)⑦M入無法確定的狀態(tài)。
  
    實現(xiàn)軟件并行化,需要更好的編程語言,需要更高層的語言抽象,使現(xiàn)有的應用能夠以一種漸進的方式轉(zhuǎn)變成并行化的應用。
  
    在編程語言中我們需要什么?需要更高層的語言抽象,包括現(xiàn)有命令式語言的發(fā)展性擴充,使現(xiàn)有的應用能夠以一種漸進的方式轉(zhuǎn)變成并行化應用。這種編程模型必須將并行化轉(zhuǎn)變成一種易于理解和分析的形式,而且不僅是在最初的開發(fā)階段,在維護階段更是如此。明示、暗示和自動并行 明示的編程模型可以提供抽象的方法,并要求編程人員能夠明確地說明并行在哪里發(fā)生。明顯表達并行的主要優(yōu)勢在于,它允許編程人員充分利用應用領域的知識,并充分表達應用中潛在的并行性。 
  
    然而,它要求新的、更高一層的編程抽象,并且在處理共享數(shù)據(jù)時需要更高層次的編程能力。暗示的編程模型將并行性隱藏在庫中或API(應用程序接口)后面,因此調(diào)用方看到的仍然是順序的形式,而由庫去執(zhí)行并行操作。它的主要缺點是,這種方法不可能實現(xiàn)某些與并行有關(guān)的性能提高。此外,這種方式也很難設計出一種在任何情況下都不顯示并行性的界面。
  
    人們還在廣泛研究另外一種方法,這就是自動并行處理。在這種方法中,編譯器將負責查找并行,通常是在那些以Fortran等傳統(tǒng)語言寫成的程序中。這種方法從表面上來看非常具有吸引力,但在實踐中并不能很好地工作。要想理解程序的潛在行為,精確的程序分析是必不可少的。即使是對于Fortran 這種簡單的語言來說,這種分析也是非常具有挑戰(zhàn)性的。此外,順序程序通常都使用順序算法,其中包含的并發(fā)特性極少。
  
    命令式和功能式語言 常用的商業(yè)編程語言(如Pascal、C、C++、Java、C#)都屬于命令式的語言,即由編程人員規(guī)定變量和數(shù)據(jù)結(jié)構(gòu)中的變化。函數(shù)(如循環(huán))、低級數(shù)據(jù)操作和共享式的可變對象實例都會使這些語言編寫而成的程序很難分析和自動并行執(zhí)行。一般人都相信,功能語言,如 Scheme、ML或Haskell可以消除這種困難,因為它們天生就適合并行運行,用這些語言編寫的程序可以操縱不可變的對象實例,而在實踐中,功能語言并不一定能夠給并行執(zhí)行帶來益處。功能程序中的并行通常是在過程調(diào)用層面上的,而且為了適應傳統(tǒng)的多處理器,這些過程被分割成了粒度非常精細的程序,幾乎達到了不切實際的地步。功能語言對并行處理的真正貢獻在于,這些語言通常使用的都是更高層次的編程風格,而在這種風格中,Map和Map-reduce 等操作會將計算應用于集合數(shù)據(jù)結(jié)構(gòu)的所有組件,這些較高層次的運算都是并行執(zhí)行的資源。例如,Google的高級工程師就曾經(jīng)描述過Google是如何使用Map-Reduce來進行大規(guī)模分布式計算的。命令式語言可以將功能擴展添加進來,這一點非常重要,為了保留目前的各類軟件中的巨大投資,用戶自然希望以漸進的方式逐步添加對并行處理的支持。
  
    更好的抽象今天的多數(shù)語言提供的是線程和鎖定層面上的明示編程方式,這些抽象都屬于低層次的。較高層次的抽象允許編程人員表達那些具備固有并行特性的任務,而運行時系統(tǒng)就可以對其進行組合和調(diào)度,使其適合實際機器上的硬件,這樣就可以使應用能夠在比較新的硬件上發(fā)揮更好的性能。
  
    高層次抽象的另外一個例子就是活動對象。活動對象在概念上運行在自己的線程上,因此創(chuàng)建1000個此類對象就相當于從概念上創(chuàng)造1000個潛在的執(zhí)行線程?;顒訉ο蟮男袨榉绞脚c監(jiān)視器非常像,但它不需要傳統(tǒng)的鎖定。相反,活動對象以外的方法調(diào)用都是異步信息,由對象對其進行匯集、排隊和傳送。
  
    開發(fā)人員已經(jīng)設計并實現(xiàn)了一些有趣的編程模型,有助于開發(fā)并行應用程序,最流行的就是用于共享內(nèi)存編程的OpenMP和用于分布式內(nèi)存編程的MPI。



  
    OpenMP程序示例
  
    OpenMP是一種工業(yè)標準的API設計規(guī)范,是由Sun、HP、IBM和Intel等多家頂級計算機廠商和軟件開發(fā)商聯(lián)手推出的,其目的在于為軟件開發(fā)人員提供一種通用的規(guī)范,使其可以很方便地設計新并行應用程序或修改及并行化現(xiàn)有串行應用程序,從而利用配置了多處理器計算系統(tǒng)的共享內(nèi)存??梢浦残砸彩荗penMP的主要目標之一,使用OpenMP開發(fā)的并行應用程序源代碼可由支持OpenMP的任何編譯器編譯,且編譯好的二進制代碼可在目標硬件平臺上運行,以獲得出色的并行性能。
  
     最流行的本地編程語言Fortran和C/C++都支持OpenMP。左圖給出了分別以C/C++和Fortran編寫的簡單OpenMP程序示例。在本例中,將y數(shù)組加到x數(shù)組這一循環(huán)迭代操作,是以并行方式執(zhí)行的。源代碼中的編譯指示、指令和編程API調(diào)用表示了OpenMP的結(jié)構(gòu)。 OpenMP 的結(jié)構(gòu)允許程序員指定并行區(qū)域、同步和數(shù)據(jù)作用域?qū)傩?,它還支持用于指定運行時配置的環(huán)境變量,例如,環(huán)境變量OMP_NUM_THREADS指定了運行時所使用的工作線程的數(shù)量。
  
    由于OpenMP編程模型專用于單一進程,因此其可伸縮性最終要受到一臺計算機中處理器(線程)數(shù)量的限制??缮炜s性還會受編程邏輯復雜程度和編程風格的限制。當前的OpenMP語義尚不夠靈活,無法處理某些應用程序。例如,當前的OpenMP規(guī)范僅允許在并行區(qū)域內(nèi)創(chuàng)建有限的動態(tài)線程。
  
    MPI(Message Passing Interface,消息傳遞接口)是一種工業(yè)標準的API規(guī)范,專為在多處理器計算機和計算機集群上獲得高性能計算而設計,該標準是由大量計算機供應商和軟件開發(fā)商共同設計的。有多種來自不同研究機構(gòu)和廠商的MPI實現(xiàn),其中最流行的一種就是MPICH,MPICH常用作為特定平臺或互連而優(yōu)化的MPI 實現(xiàn)的編碼基礎。MPI實現(xiàn)依然在不斷發(fā)展。MPI-1支持一些關(guān)鍵特性,如點到點及與通信設備的集群消息通信。一條消息中可包含基本數(shù)據(jù)類型或派生(用戶定義的)數(shù)據(jù)類型的MPI數(shù)據(jù),它還支持互連拓撲。MPI-2則提供許多高級通信特性,如遠程內(nèi)存訪問和單端通信等,還支持動態(tài)進程創(chuàng)建、管理和并行 I/O。
  
    總體而言,MPI為計算機集群上的并行應用程序提供了一個出色的解決方案,但對于許多開發(fā)人員來說,MPI也是一種困難的編程模型,因為MPI 的通信延遲時間較長,所以必須合理分割程序的核心邏輯,以使分布成本更為合理。分析及劃分應用程序問題,并將問題映射到分布式進程集合中,絕對不是一項可靠直覺完成的任務,因為許多MPI進程之間的交互作用都相當復雜,因此即便選用了恰當?shù)墓ぞ?,調(diào)試并調(diào)整運行在大量節(jié)點上的MPI應用程序也極具挑戰(zhàn)性。 MPI的性能取決于底層硬件平臺和互連,在某些極端的情況下,MPI應用程序的性能可能會受到繁重的互連流量的影響。另外一個嚴重的問題就是大規(guī)模MPI 應用程序的可靠性。對于許多MPI實現(xiàn)而言,只要單一計算節(jié)點發(fā)生故障,無法正確響應,MPI程序就會停止工作。(本文內(nèi)容由Sun軟件架構(gòu)師 Liang T?Chen和微軟軟件架構(gòu)師Herb Sutter提供)
  
    在服務器端,RISC架構(gòu)的服務器進入多核技術(shù)領域要早一些,所以一些專有的、或者說是基于Unix操作系統(tǒng)的行業(yè)化特征非常顯著的應用已經(jīng)是并行化編寫的。而當x86領域迎來64位計算和多核技術(shù)的時候,絕大多數(shù)的基于Windows的應用都是傳統(tǒng)的按照單一線程開發(fā)的。
  
    現(xiàn)在,標準化、開放、TCO等浪潮席卷整個計算領域,x86服務器市場在飛速發(fā)展,逐步擠壓原來的、非常強勢的RISC架構(gòu)服務器占有的份額,跟隨服務器硬件技術(shù)的發(fā)展腳步,基于x86服務器的操作系統(tǒng)以及上層應用,都需要考慮處理器級別的多核、并行計算設計帶來的性能提升,考慮軟件如何能夠適應并且更充分發(fā)揮硬件架構(gòu)的優(yōu)勢。應該說,這是軟件層進入了大規(guī)模的并行化設計階段,畢竟,在銷售量方面,x86市場是絕對領先的。
  
    記者之前接觸過一些將應用改為并行運行的案例,是采用集群系統(tǒng)后,將原來的應用進行一些修改以便能夠充分利用集群系統(tǒng)并行處理的優(yōu)勢,比如石油勘探行業(yè)的用戶,他們在計算和分析地震勘探資料應用中,采用大規(guī)模并行計算系統(tǒng)來實現(xiàn)疊前偏移和精確地震成像處理,提高勘探開發(fā)效益。不過,這些應用都還處在一個剛剛開始的并行化應用階段,采用的是集群系統(tǒng)。多核技術(shù)在x86服務器中成熟后,如何能夠充分利用到多核并行處理的優(yōu)勢,才是軟件層面真正的挑戰(zhàn),就是所謂的“線程級并行”。
  
    處理器在并行計算方面所做出的技術(shù)革新貢獻,僅僅是整個產(chǎn)業(yè)鏈上的一個小環(huán)節(jié),與用戶應用緊密相連的是軟件層,正如文章中引述的微軟軟件架構(gòu)師Sutter所說的:處理器設計首要的著眼點應該是可編程性,而不是速度。

分享到

多易

相關(guān)推薦