自編教材分享:第八章—訪存優(yōu)化(三)



緩存優(yōu)化
減少偽共享
為解決數(shù)據(jù)一致性的問題,需要處理器各個核心訪問緩存時都遵循緩存一致性協(xié)議。即當(dāng)某個處理器核心修改緩存行數(shù)據(jù)時,其它的處理器核通過監(jiān)聽機制獲悉并使其共享緩存行失效,此時該處理器核心將修改后的緩存行寫回到主內(nèi)存中。若其它處理器核需要此緩存行共享數(shù)據(jù),則從主內(nèi)存中重新加載,并放入緩存,這樣可以保證讀取數(shù)據(jù)的正確性,如圖所示。

在多核CPU系統(tǒng)中,由于要操作的不同變量處于同一緩存行,某核心更新緩存行中數(shù)據(jù)并將其寫回緩存,同時其它核心會使該緩存行失效,使用時需要從內(nèi)存中重新加載,這種情況就是緩存行的偽共享問題,偽共享會導(dǎo)致大量的緩存沖突,應(yīng)盡量避免。

多線程編程時往往不可避免的要遇到數(shù)據(jù)共享,編程時應(yīng)該注意如下原則:
盡量少的使用共享數(shù)據(jù),可以將不同線程操作的數(shù)據(jù)分配在不同的緩存行中,或者進(jìn)行緩存行填充,避免多個線程共享同一緩存行內(nèi)的數(shù)據(jù)。
在多個核心共享同一緩存行數(shù)據(jù)時,如果不進(jìn)行對緩存的寫入,是不會發(fā)生偽共享問題的,所以應(yīng)當(dāng)盡量少的修改數(shù)據(jù)。例如上述的例子中,若核心0與核心1頻繁地對Arr[0]以及Arr[1]進(jìn)行修改,代價是非常高的。
在內(nèi)核版本4.1.0及以上的Linux系統(tǒng)中,perf工具包含了一些對程序中偽共享的分析功能,稱為perf c2c。perf c2c可以收集高速緩存的性能數(shù)據(jù),之后基于采樣數(shù)據(jù)生成報告,以此代碼為例說明如何使用perf c2c進(jìn)行偽共享分析。
sum數(shù)組大小為64字節(jié),在實驗測試平臺中恰好占用一個緩存行。當(dāng)8個線程同時對該緩存行內(nèi)的sum數(shù)組進(jìn)行寫時,程序?qū)⒉豢杀苊獾匕l(fā)生偽共享現(xiàn)象。使用perf c2c對該程序的執(zhí)行過程進(jìn)行分析,在Linux中輸入以下指令。
生成的報告如下所示,為更清晰的予以說明,對表中的輸出結(jié)果適當(dāng)進(jìn)行了簡化。
為避免多個線程頻繁訪問同一緩存行,減少偽共享問題,修改后的程序如代碼所示。修改后的程序避免了多個線程同時頻繁地訪問同一個緩存行,此時再次使用pref c2c分析,可以發(fā)現(xiàn)程序中的偽共享現(xiàn)象大幅度減少。
優(yōu)化后結(jié)果如下:
數(shù)據(jù)預(yù)取
數(shù)據(jù)預(yù)取將待處理器所需的數(shù)據(jù)提前加載到緩存中,避免緩存不命中的情況出現(xiàn)。按照數(shù)據(jù)預(yù)取的效果,可將數(shù)據(jù)預(yù)取分為無預(yù)取、理想預(yù)取和非理想預(yù)取三種,如圖所示。

硬件預(yù)取與軟件預(yù)取
預(yù)取的實現(xiàn)通常有軟件預(yù)取和硬件預(yù)取兩種方式。硬件預(yù)取是提前把指令和數(shù)據(jù)預(yù)取到緩存中的一種硬件機制,其不需要使用預(yù)取指令或額外的編寫代碼。但硬件預(yù)取必需建立在能動態(tài)進(jìn)行程序執(zhí)行分支預(yù)測的基礎(chǔ)上,所以預(yù)取的范圍很小,且會增加系統(tǒng)整體開銷。
在絕大部分情況下,程序?qū)?nèi)存的訪問模式是隨機的、不規(guī)則的。此時需要使用軟件預(yù)取,通過插入的預(yù)取指令,提前把數(shù)據(jù)取入緩存,這種方法對系統(tǒng)開銷的影響較小,也不會減慢訪問緩存的速度,是一種靈活有效的數(shù)據(jù)預(yù)取方式。
本節(jié)主要介紹軟件預(yù)取對程序性能的影響,軟件預(yù)取通過插入預(yù)取指令或者函數(shù)實現(xiàn),使用預(yù)取函數(shù)__builtin_prefetch(),該函數(shù)原型為void__builtin_prefetch (const void*addr,rw,locality),在測試用例合適位置插入預(yù)取指令,使用LLVM7-1版本的編譯器對加入預(yù)取指令的程序進(jìn)行編譯,結(jié)果顯示加入預(yù)取指令比未加入指令的執(zhí)行時間快了13%。

自編教材分享:第八章—訪存優(yōu)化(三)的評論 (共 條)
