日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

产生线程安全的原因(2)(操作系统)

發(fā)布時間:2023/12/19 windows 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 产生线程安全的原因(2)(操作系统) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

3.3.2 Cache的性能測試

用于測試程序的數(shù)據(jù)可以模擬一個任意大小的工作集:包括讀、寫訪問,隨機(jī)、連續(xù)訪問。在圖3.4中我們可以看到,程序為工作集創(chuàng)建了一個與其大小和元素類型相同的數(shù)組:

struct l {struct l *n;long int pad[NPAD]; };

n字段將所有節(jié)點隨機(jī)得或者順序的加入到環(huán)形鏈表中,用指針從當(dāng)前節(jié)點進(jìn)入到下一個節(jié)點。pad字段用來存儲數(shù)據(jù),其可以是任意大小。在一些測試程序中,pad字段是可以修改的, 在其他程序中,pad字段只可以進(jìn)行讀操作。

在性能測試中,我們談到工作集大小的問題,工作集使用結(jié)構(gòu)體l定義的元素表示的。2N?字節(jié)的工作集包含


個元素. 顯然sizeof(struct l) 的值取決于NPAD的大小。在32位系統(tǒng)上,NPAD=7意味著數(shù)組的每個元素的大小為32字節(jié),在64位系統(tǒng)上,NPAD=7意味著數(shù)組的每個元素的大小為64字節(jié)。

單線程順序訪問

最簡單的情況就是遍歷鏈表中順序存儲的節(jié)點。無論是從前向后處理,還是從后向前,對于處理器來說沒有什么區(qū)別。下面的測試中,我們需要得到處理鏈表中一個元素所需要的時間,以CPU時鐘周期最為計時單元。圖3.10顯示了測試結(jié)構(gòu)。除非有特殊說明, 所有的測試都是在Pentium?4 64-bit 平臺上進(jìn)行的,因此結(jié)構(gòu)體l中NPAD=0,大小為8字節(jié)。


一開始的兩個測試數(shù)據(jù)收到了噪音的污染。由于它們的工作負(fù)荷太小,無法過濾掉系統(tǒng)內(nèi)其它進(jìn)程對它們的影響。我們可以認(rèn)為它們都是4個周期以內(nèi)的。這樣一來,整個圖可以劃分為比較明顯的三個部分:

  • 工作集小于214字節(jié)的。
  • 工作集從215字節(jié)到220字節(jié)的。
  • 工作集大于221字節(jié)的。

這樣的結(jié)果很容易解釋——是因為處理器有16KB的L1d和1MB的L2。而在這三個部分之間,并沒有非常銳利的邊緣,這是因為系統(tǒng)的其它部分也在使用緩存,我們的測試程序并不能獨占緩存的使用。尤其是L2,它是統(tǒng)一式的緩存,處理器的指令也會使用它(注: Intel使用的是包容式緩存)。

測試的實際耗時可能會出乎大家的意料。L1d的部分跟我們預(yù)想的差不多,在一臺P4上耗時為4個周期左右。但L2的結(jié)果則出乎意料。大家可能覺得需要14個周期以上,但實際只用了9個周期。這要歸功于處理器先進(jìn)的處理邏輯,當(dāng)它使用連續(xù)的內(nèi)存區(qū)時,會?預(yù)先讀取下一條緩存線的數(shù)據(jù)。這樣一來,當(dāng)真正使用下一條線的時候,其實已經(jīng)早已讀完一半了,于是真正的等待耗時會比L2的訪問時間少很多。

在工作集超過L2的大小之后,預(yù)取的效果更明顯了。前面我們說過,主存的訪問需要耗時200個周期以上。但在預(yù)取的幫助下,實際耗時保持在9個周期左右。200 vs 9,效果非常不錯。

我們可以觀察到預(yù)取的行為,至少可以間接地觀察到。圖3.11中有4條線,它們表示處理不同大小結(jié)構(gòu)時的耗時情況。隨著結(jié)構(gòu)的變大,元素間的距離變大了。圖中4條線對應(yīng)的元素距離分別是0、56、120和248字節(jié)。

圖中最下面的這一條線來自前一個圖,但在這里更像是一條直線。其它三條線的耗時情況比較差。圖中這些線也有比較明顯的三個階段,同時,在小工作集的情況下也有比較大的錯誤(請再次忽略這些錯誤)。在只使用L1d的階段,這些線條基本重合。因為這時候還不需要預(yù)取,只需要訪問L1d就行。

在L2階段,三條新加的線基本重合,而且耗時比老的那條線高很多,大約在28個周期左右,差不多就是L2的訪問時間。這表明,從L2到L1d的預(yù)取并沒有生效。這是因為,對于最下面的線(NPAD=0),由于結(jié)構(gòu)小,8次循環(huán)后才需要訪問一條新緩存線,而上面三條線對應(yīng)的結(jié)構(gòu)比較大,拿相對最小的NPAD=7來說,光是一次循環(huán)就需要訪問一條新線,更不用說更大的NPAD=15和31了。而預(yù)取邏輯是無法在每個周期裝載新線的,因此每次循環(huán)都需要從L2讀取,我們看到的就是從L2讀取的時延。

更有趣的是工作集超過L2容量后的階段。快看,4條線遠(yuǎn)遠(yuǎn)地拉開了。元素的大小變成了主角,左右了性能。處理器應(yīng)能識別每一步(stride)的大小,不去為NPAD=15和31獲取那些實際并不需要的緩存線(參見6.3.1)。元素大小對預(yù)取的約束是根源于硬件預(yù)取的限制——它無法跨越頁邊界。如果允許預(yù)取器跨越頁邊界,而下一頁不存在或無效,那么OS還得去尋找它。這意味著,程序需要遭遇一次并非由它自己產(chǎn)生的頁錯誤,這是完全不能接受的。在NPAD=7或者更大的時候,由于每個元素都至少需要一條緩存線,預(yù)取器已經(jīng)幫不上忙了,它沒有足夠的時間去從內(nèi)存裝載數(shù)據(jù)。 另一個導(dǎo)致慢下來的原因是TLB緩存的未命中。TLB是存儲虛實地址映射的緩存,參見第4節(jié)。為了保持快速,TLB只有很小的容量。如果有大量頁被反復(fù)訪問,超出了TLB緩存容量,就會導(dǎo)致反復(fù)地進(jìn)行地址翻譯,這會耗費大量時間。TLB查找的代價分?jǐn)偟剿性厣?#xff0c;如果元素越大,那么元素的數(shù)量越少,每個元素承擔(dān)的那一份就越多。

為了觀察TLB的性能,我們可以進(jìn)行另兩項測試。第一項:我們還是順序存儲列表中的元素,使NPAD=7,讓每個元素占滿整個cache line,第二項:我們將列表的每個元素存儲在一個單獨的頁上,忽略每個頁沒有使用的部分以用來計算工作集的大小。(這樣做可能不太一致,因為在前面的測試中,我計算了結(jié)構(gòu)體中每個元素沒有使用的部分,從而用來定義NPAD的大小,因此每個元素占滿了整個頁,這樣以來工作集的大小將會有所不同。但是這不是這項測試的重點,預(yù)取的低效率多少使其有點不同)。結(jié)果表明,第一項測試中,每次列表的迭代都需要一個新的cache line,而且每64個元素就需要一個新的頁。第二項測試中,每次迭代都會在一個新的頁中加載一個新的cache line。

結(jié)果見圖3.12。該測試與圖3.11是在同一臺機(jī)器上進(jìn)行的。基于可用RAM空間的有限性,測試設(shè)置容量空間大小為2的24次方字節(jié),這就需要1GB的容量將對象放置在分頁上。圖3.12中下方的紅色曲線正好對應(yīng)了圖3.11中NPAD等于7的曲線。我們看到不同的步長顯示了高速緩存L1d和L2的大小。第二條曲線看上去完全不同,其最重要的特點是當(dāng)工作容量到達(dá)2的13次方字節(jié)時開始大幅度增長。這就是TLB緩存溢出的時候。我們能計算出一個64字節(jié)大小的元素的TLB緩存有64個輸入。成本不會受頁面錯誤影響,因為程序鎖定了存儲器以防止內(nèi)存被換出。

可以看出,計算物理地址并把它存儲在TLB中所花費的周期數(shù)量級是非常高的。圖3.12的表格顯示了一個極端的例子,但從中可以清楚的得到:TLB緩存效率降低的一個重要因素是大型NPAD值的減緩。由于物理地址必須在緩存行能被L2或主存讀取之前計算出來,地址轉(zhuǎn)換這個不利因素就增加了內(nèi)存訪問時間。這一點部分解釋了為什么NPAD等于31時每個列表元素的總花費比理論上的RAM訪問時間要高。

通過查看鏈表元素被修改時測試數(shù)據(jù)的運行情況,我們可以窺見一些更詳細(xì)的預(yù)取實現(xiàn)細(xì)節(jié)。圖3.13顯示了三條曲線。所有情況下元素寬度都為16個字節(jié)。第一條曲線“Follow”是熟悉的鏈表走線在這里作為基線。第二條曲線,標(biāo)記為“Inc”,僅僅在當(dāng)前元素進(jìn)入下一個前給其增加thepad[0]成員。第三條曲線,標(biāo)記為”Addnext0″, 取出下一個元素的thepad[0]鏈表元素并把它添加為當(dāng)前鏈表元素的thepad[0]成員。

在沒運行時,大家可能會以為”Addnext0″更慢,因為它要做的事情更多——在沒進(jìn)到下個元素之前就需要裝載它的值。但實際的運行結(jié)果令人驚訝——在某些小工作集下,”Addnext0″比”Inc”更快。這是為什么呢?原因在于,系統(tǒng)一般會對下一個元素進(jìn)行強(qiáng)制性預(yù)取。當(dāng)程序前進(jìn)到下個元素時,這個元素其實早已被預(yù)取在L1d里。因此,只要工作集比L2小,”Addnext0″的性能基本就能與”Follow”測試媲美。

但是,”Addnext0″比”Inc”更快離開L2,這是因為它需要從主存裝載更多的數(shù)據(jù)。而在工作集達(dá)到2?21字節(jié)時,”Addnext0″的耗時達(dá)到了28個周期,是同期”Follow”14周期的兩倍。這個兩倍也很好解釋。”Addnext0″和”Inc”涉及對內(nèi)存的修改,因此L2的逐出操作不能簡單地把數(shù)據(jù)一扔了事,而必須將它們寫入內(nèi)存。因此FSB的可用帶寬變成了一半,傳輸?shù)攘繑?shù)據(jù)的耗時也就變成了原來的兩倍。

決定順序式緩存處理性能的另一個重要因素是緩存容量。雖然這一點比較明顯,但還是值得一說。圖3.14展示了128字節(jié)長元素的測試結(jié)果(64位機(jī),NPAD=15)。這次我們比較三臺不同計算機(jī)的曲線,兩臺P4,一臺Core 2。兩臺P4的區(qū)別是緩存容量不同,一臺是32k的L1d和1M的L2,一臺是16K的L1d、512k的L2和2M的L3。Core 2那臺則是32k的L1d和4M的L2。

圖中最有趣的地方,并不是Core 2如何大勝兩臺P4,而是工作集開始增長到連末級緩存也放不下、需要主存熱情參與之后的部分。

與我們預(yù)計的相似,最末級緩存越大,曲線停留在L2訪問耗時區(qū)的時間越長。在220字節(jié)的工作集時,第二臺P4(更老一些)比第一臺P4要快上一倍,這要完全歸功于更大的末級緩存。而Core 2拜它巨大的4M L2所賜,表現(xiàn)更為卓越。

對于隨機(jī)的工作負(fù)荷而言,可能沒有這么驚人的效果,但是,如果我們能將工作負(fù)荷進(jìn)行一些裁剪,讓它匹配末級緩存的容量,就完全可以得到非常大的性能提升。也是由于這個原因,有時候我們需要多花一些錢,買一個擁有更大緩存的處理器。

單線程隨機(jī)訪問模式的測量

前面我們已經(jīng)看到,處理器能夠利用L1d到L2之間的預(yù)取消除訪問主存、甚至是訪問L2的時延。

但是,如果換成隨機(jī)訪問或者不可預(yù)測的訪問,情況就大不相同了。圖3.15比較了順序讀取與隨機(jī)讀取的耗時情況。

換成隨機(jī)之后,處理器無法再有效地預(yù)取數(shù)據(jù),只有少數(shù)情況下靠運氣剛好碰到先后訪問的兩個元素挨在一起的情形。

圖3.15中有兩個需要關(guān)注的地方。首先,在大的工作集下需要非常多的周期。這臺機(jī)器訪問主存的時間大約為200-300個周期,但圖中的耗時甚至超過了450個周期。我們前面已經(jīng)觀察到過類似現(xiàn)象(對比圖3.11)。這說明,處理器的自動預(yù)取在這里起到了反效果。

其次,代表隨機(jī)訪問的曲線在各個階段不像順序訪問那樣保持平坦,而是不斷攀升。為了解釋這個問題,我們測量了程序在不同工作集下對L2的訪問情況。結(jié)果如圖3.16和表3.2。

從圖中可以看出,當(dāng)工作集大小超過L2時,未命中率(L2未命中次數(shù)/L2訪問次數(shù))開始上升。整條曲線的走向與圖3.15有些相似: 先急速爬升,隨后緩緩下滑,最后再度爬升。它與耗時圖有緊密的關(guān)聯(lián)。L2未命中率會一直爬升到100%為止。只要工作集足夠大(并且內(nèi)存也足夠大),就可以將緩存線位于L2內(nèi)或處于裝載過程中的可能性降到非常低。

緩存未命中率的攀升已經(jīng)可以解釋一部分的開銷。除此以外,還有一個因素。觀察表3.2的L2/#Iter列,可以看到每個循環(huán)對L2的使用次數(shù)在增長。由于工作集每次為上一次的兩倍,如果沒有緩存的話,內(nèi)存的訪問次數(shù)也將是上一次的兩倍。在按順序訪問時,由于緩存的幫助及完美的預(yù)見性,對L2使用的增長比較平緩,完全取決于工作集的增長速度。


而換成隨機(jī)訪問后,單位耗時的增長超過了工作集的增長,根源是TLB未命中率的上升。圖3.17描繪的是NPAD=7時隨機(jī)訪問的耗時情況。這一次,我們修改了隨機(jī)訪問的方式。正常情況下是把整個列表作為一個塊進(jìn)行隨機(jī)(以∞表示),而其它11條線則是在小一些的塊里進(jìn)行隨機(jī)。例如,標(biāo)簽為’60′的線表示以60頁(245760字節(jié))為單位進(jìn)行隨機(jī)。先遍歷完這個塊里的所有元素,再訪問另一個塊。這樣一來,可以保證任意時刻使用的TLB條目數(shù)都是有限的。 NPAD=7對應(yīng)于64字節(jié),正好等于緩存線的長度。由于元素順序隨機(jī),硬件預(yù)取不可能有任何效果,特別是在元素較多的情況下。這意味著,分塊隨機(jī)時的L2未命中率與整個列表隨機(jī)時的未命中率沒有本質(zhì)的差別。隨著塊的增大,曲線逐漸逼近整個列表隨機(jī)對應(yīng)的曲線。這說明,在這個測試?yán)?#xff0c;性能受到TLB命中率的影響很大,如果我們能提高TLB命中率,就能大幅度地提升性能(在后面的一個例子里,性能提升了38%之多)。



總結(jié)

以上是生活随笔為你收集整理的产生线程安全的原因(2)(操作系统)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。