新聞中心
在上月舉行的PDC 09大會上,微軟并行庫團隊的開發(fā)工程師Igor Ostrovsky介紹了PLINQ的工作原理,以及多核編程中,尤其是在PLINQ使用過程中幾種常見性能問題及應(yīng)對方法。Igor表示,這些性能問題很少在順序編程中遇到,因此在并行環(huán)境中容易被人忽視。

公司主營業(yè)務(wù):成都網(wǎng)站建設(shè)、做網(wǎng)站、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出柳北免費做網(wǎng)站回饋大家。
#T#
第一個性能問題是內(nèi)存分配
由于利用了多核CPU進行運算,對象分配的速度也加快了。此外,程序中可以還會出現(xiàn)更高頻率的字符串連接或裝箱操作,這都會使GC壓力增大。.NET應(yīng)用程序所使用的默認GC方式為Concurrent GC,它的性能很高,并且為降低應(yīng)用程序的延遲作了很多優(yōu)化。它的最佳使用場景是用戶交互式應(yīng)用,這樣可以盡可能避免用戶界面的停頓,但是它在長期運行的多核程序中表現(xiàn)并不好。而最終的結(jié)果是大量計算時間耗費在GC上,此時應(yīng)用程序算法即便是利用了多個核,也會發(fā)現(xiàn)它的伸縮能力受到了GC限制。解決這個問題的方法之一是減小內(nèi)存分配,例如可以使用值類型來代替引用類型。值類型的對象會分配在線程棧而不是堆上,以此避免對GC產(chǎn)生壓力。第二個方法是在config文件中啟用Server GC。使用Server GC會改變.NET分配對象的方式,此時.NET會為每個核準備不同的堆,并且獨立進行垃圾回收。這樣在一臺4核的機器上便可以有4個線程同時進行垃圾回收,性能自然也就隨著多核而提升了。
第二個性能問題是CPU在局部化(Locality)和緩存方面的問題
在流行的多核架構(gòu)中,每個核都有獨立的二級緩存。CPU并不會緩存單個地址中的數(shù)據(jù),而是緩存以64字節(jié)或128字節(jié)相鄰內(nèi)存的緩存條目(cache line),因此當某個核改變了內(nèi)存中的數(shù)據(jù)時,則其他核中地址相鄰的緩存數(shù)據(jù)也會失效,這樣CPU每次進行計算時都要從速度較慢的內(nèi)存中加載數(shù)據(jù)。這個性能問題的隱蔽之處在于代碼中的不同數(shù)據(jù)——例如同一個數(shù)組的不同下標——可能在內(nèi)存中處在同一個緩存條目中,因此這個問題又被稱為錯誤共享(False Sharing)。Igor演示了一段性能低下的代碼,在這個實現(xiàn)中多個線程會不斷讀寫同一個數(shù)組的相鄰下標,因此造成了錯誤共享。Igor的修改方法是將數(shù)據(jù)存放在數(shù)組中相距較遠的下標,甚至是不同的數(shù)組中。由于CPU的緩存條目大小有限,這種方法可以避免出現(xiàn)錯誤共享。博客園老趙在《計算機體系結(jié)構(gòu)與程序性能》一文中也提出了一種優(yōu)化方式,他的做法是盡可能使用局部變量來保存計算過程中的中間值,以此減少對數(shù)組的修改操作。由于局部變量分處不同線程的??臻g內(nèi),因此地址相距很遠,不會造成錯誤共享問題。當有人問起到這種優(yōu)化方式是否安全時,Igor答到,這其實和CPU架構(gòu)的實現(xiàn)方式有很大關(guān)系。如果某一天緩存實現(xiàn)變化了,可能這種優(yōu)化方式會適得其反。不過在目前主流架構(gòu)中,這樣的做法是比較安全的。Igor補充道,他認為這也是為什么“全自動”并行化那么困難的原因之一,因為在并行環(huán)境下影響程序性能的方面實在太多了。
第三個問題在于開發(fā)人員傾向于在PLINQ中使用大量小粒度的委托來完成工作
此時每個委托的計算任務(wù)很小,而委托的執(zhí)行次數(shù)會很多。在計算較長的序列時,小粒度的委托對象也能獲得性能提高,但是它會產(chǎn)生額外的負載。例如,MoveNext和Current的調(diào)用,以及每個委托的執(zhí)行性能都和虛方法比較接近。此外,一個較長的輸入序列也會受限于內(nèi)存的吞吐量。因此,Igor建議開發(fā)人員在使用PLINQ時盡可能使用計算量較大的委托,以此減少計算主體外的性能開銷。
第四和第五問題則與PLINQ的實現(xiàn)有關(guān)
Igor表示,PLINQ可以并行執(zhí)行所有的LINQ查詢,但是相對于復(fù)雜的LINQ查詢,PLINQ能夠?qū)唵蔚腖INQ操作有更好的優(yōu)化。因此,Igor建議開發(fā)人員在使用PLINQ時可以手動將復(fù)雜的LINQ表達式拆分為簡單的LINQ查詢,并且只在真正需要大量計算的地方才開始并行化。這種結(jié)合順序執(zhí)行和并行執(zhí)行的方式,可以讓應(yīng)用程序的性能達到最優(yōu)。此外,為不同的輸入方式選擇不同的分塊(partition)策略對性能的影響很大,因此PLINQ會對數(shù)組和IList<>進行靜態(tài)的分割,而對IEnumerable<>集合按實際需求進行劃分,而開發(fā)人員也可以通過自定義Partitioner的方式來指定特別的分割策略。
最后,Igor強調(diào),使用并行計算進行程序性能優(yōu)化之前,一定要通過合適的評測方式來找到代碼的瓶頸。如果這個瓶頸正符合數(shù)據(jù)并行(data parallel)模式,那么可以使用PLINQ進行性能優(yōu)化。而優(yōu)化完成后還需要評測其效果,并使用之前提出的幾種方案進行合適的調(diào)整。
延伸閱讀
PLINQ(Parallel LINQ)。微軟對PLINQ在Parallel FX中的定位是:PLINQ是TPL(Task Parallel Library)的一個高層應(yīng)用。目前PLINQ已經(jīng)被集成到.NET 4.0當中了。
網(wǎng)站標題:PLINQ并行開發(fā)中常見性能問題及應(yīng)對方案
文章源于:http://m.fisionsoft.com.cn/article/djieode.html


咨詢
建站咨詢
