<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      軟硬件協(xié)同編程 - C#玩轉(zhuǎn)CPU高速緩存(附示例)

      寫在前面

      好久沒有寫博客了,一直在不斷地探索響應(yīng)式DDD,又get到了很多新知識(shí),解惑了很多老問題,最近讀了Martin Fowler大師一篇非常精彩的博客The LMAX Architecture,里面有一個(gè)術(shù)語(yǔ)Mechanical Sympathy,姑且翻譯成軟硬件協(xié)同編程(Hardware and software working together in harmony),很有感悟,說的是要把編程與底層硬件協(xié)同起來,這樣對(duì)于開發(fā)低延遲、高并發(fā)的系統(tǒng)特別地重要,為什么呢,今天我們就來講講CPU的高速緩存。

      電腦的緩存系統(tǒng)


      電腦的緩存系統(tǒng)分了很多層級(jí),從外到內(nèi)依次是主內(nèi)存、三級(jí)高速緩存、二級(jí)高速緩存、一級(jí)高速緩存,所以,在我們的腦海里,覺點(diǎn)磁盤的讀寫速度是很慢的,而內(nèi)存的讀寫速度確是快速的,的確如此,從上圖磁盤和內(nèi)存距離CPU的遠(yuǎn)近距離就看出來。這里先說明一個(gè)概念,主內(nèi)存被所有CPU共享;三級(jí)緩存被同一個(gè)插槽內(nèi)的CPU所共享;單個(gè)CPU獨(dú)享自己的一級(jí)、二級(jí)緩存,即高速緩存。CPU是真正做事情的地方,它會(huì)先從高速緩存中去獲取所需的數(shù)據(jù),如果找不到,再去三級(jí)緩存中查找,如果還是找不到最終就去會(huì)主內(nèi)存查找,并且找到數(shù)據(jù)后,先要復(fù)制到緩存(L1、L2、L3),然后在返回?cái)?shù)據(jù);如果每一次都這樣來來回回地復(fù)制和讀取數(shù)據(jù),那么無疑是非常耗時(shí)。如果能夠把數(shù)據(jù)緩存到高速緩存中就好了,這樣不僅CPU第一次就可以直接從高速緩存中命中數(shù)據(jù),而且每個(gè)CPU都獨(dú)占自己的高速緩存,多線程下也不存在臨界資源的問題,這才是真正的低延遲,但是這個(gè)地方對(duì)高層開發(fā)人員而言根本不透明,腫么辦?

      對(duì)于CPU而言,只有第一、二、三級(jí)才是緩存區(qū),主內(nèi)存不是,如果需要到主內(nèi)存讀取數(shù)據(jù),這種情況稱為緩存未命中(cache miss)。

      探索高速緩存的構(gòu)造

      我們先來看一張使用魯大師檢測(cè)的處理器信息截圖,如下:

      從上圖可以看到,CPU高速緩存(一、二級(jí))的存儲(chǔ)單元為L(zhǎng)ine,大小為64 bytes,也就是說無論我們的數(shù)據(jù)大小是多少,高速緩存都是以64 bytes為單位緩存數(shù)據(jù),比如一個(gè)8位的long類型數(shù)組,即使只有第一位有數(shù)據(jù),每次高速緩存加載數(shù)據(jù)的時(shí)候,都會(huì)順帶把后面7位數(shù)據(jù)也一起加載(因?yàn)閿?shù)組內(nèi)元素的內(nèi)存地址是連續(xù)的),這就是底層硬件CPU的工作機(jī)制,所以我們要利用這個(gè)天然的優(yōu)勢(shì),讓數(shù)據(jù)獨(dú)占整個(gè)緩存行,這樣CPU命中的緩存行中就一定有我們的數(shù)據(jù)。

      示例

      使用不同的線程數(shù),對(duì)一個(gè)long類型的數(shù)值計(jì)數(shù)500億次。

      備注:統(tǒng)計(jì)分析圖表和總結(jié)在最后。

      1. 一般的實(shí)現(xiàn)方式

      大多數(shù)程序員都會(huì)這樣子構(gòu)造數(shù)據(jù),老鐵沒毛病。

      代碼

      ///// <summary>
      ///// CPU偽共享高速緩存行條目(偽共享)
      ///// </summary>
      public class FalseSharingCacheLineEntry
      {
          public long Value = 0L;
      }
      

      單線程


      平均響應(yīng)時(shí)間 = 1508.56 毫秒。

      雙線程


      平均響應(yīng)時(shí)間 = 4460.40 毫秒。

      三線程


      平均響應(yīng)時(shí)間 = 7719.02 毫秒。

      四線程


      平均響應(yīng)時(shí)間 = 10404.30 毫秒。

      2. 獨(dú)占緩存行,直接命中高速緩存。

      2.1 直接填充

      代碼

      /// <summary>
      /// CPU高速緩存行條目(直接填充)
      /// </summary>
      public class CacheLineEntry
      {
          protected long P1, P2, P3, P4, P5, P6, P7;
          public long Value = 0L;
          protected long P9, P10, P11, P12, P13, P14, P15;
      }
      

      為了保證高速緩存行中一定有我們的數(shù)據(jù),所以前后都填充7個(gè)long。

      單線程


      平均響應(yīng)時(shí)間 = 1516.33 毫秒。

      雙線程


      平均響應(yīng)時(shí)間 = 1529.97 毫秒。

      三線程


      平均響應(yīng)時(shí)間 = 1563.65 毫秒。

      四線程


      平均響應(yīng)時(shí)間 = 1616.12 毫秒。

      2.2 內(nèi)存布局填充

      作為一個(gè)C#程序員,必須寫出優(yōu)雅的代碼,可以使用StructLayout、FieldOffset來控制class、struct的內(nèi)存布局。

      備注:就是上面直接填充的優(yōu)雅實(shí)現(xiàn)方式而已。

      代碼

      /// <summary>
      /// CPU高速緩存行條目(控制內(nèi)存布局)
      /// </summary>
      [StructLayout(LayoutKind.Explicit, Size = 120)]
      public class CacheLineEntryOne
      {
          [FieldOffset(56)]
          private long _value;
      
          public long Value
          {
              get => _value;
              set => _value = value;
          }
      }
      

      單線程


      平均響應(yīng)時(shí)間 = 2008.12 毫秒。

      雙線程


      平均響應(yīng)時(shí)間 = 2046.33 毫秒。

      三線程


      平均響應(yīng)時(shí)間 = 2081.75 毫秒。

      四線程


      平均響應(yīng)時(shí)間 = 2163.092 毫秒。

      3. 統(tǒng)計(jì)分析


      上面的圖表已經(jīng)一目了然了吧,一般實(shí)現(xiàn)方式的持續(xù)時(shí)間隨線程數(shù)呈線性增長(zhǎng),多線程下表現(xiàn)的非常糟糕,而通過直接、內(nèi)存布局方式填充了數(shù)據(jù)后,響應(yīng)時(shí)間與線程數(shù)的多少?zèng)]有無關(guān),達(dá)到了真正的低延遲。其中直接填充數(shù)據(jù)的方式,效率最高,內(nèi)存布局方式填充次之,在四線程的情況下,一般實(shí)現(xiàn)方式持續(xù)時(shí)間為10.4秒多,直接填充數(shù)據(jù)的方式為1.6秒,內(nèi)存布局填充方式為2.2秒,延遲還是比較明顯,為什么會(huì)有這么大的差距呢?

      刨根問底

      在C#下,一個(gè)long類型占8 byte,對(duì)于一般的實(shí)現(xiàn)方式,在多線程的情況下,隸屬于每個(gè)獨(dú)立線程的數(shù)據(jù)會(huì)共用同一個(gè)緩存行,所以只要有一個(gè)線程更新了緩存行的數(shù)據(jù),那么整個(gè)緩存行就自動(dòng)失效,這樣就導(dǎo)致CPU永遠(yuǎn)無法直接從高速緩存中命中數(shù)據(jù),每次都要經(jīng)過一、二、三級(jí)緩存到主內(nèi)存中重新獲取數(shù)據(jù),時(shí)間就是被浪費(fèi)在了這樣的來來回回中。而對(duì)數(shù)據(jù)進(jìn)行填充后,隸屬于每個(gè)獨(dú)立線程的數(shù)據(jù)不僅被緩存到了CPU的高速緩存中,而且每個(gè)數(shù)據(jù)都獨(dú)占整個(gè)緩存行,其他的線程更新數(shù)據(jù),并不會(huì)導(dǎo)致自己的緩存行失效,所以每次CPU都可以直接命中,不管是單線程也好,還是多線程也好,只要線程數(shù)小于等于CPU的核數(shù)都和單線程一樣的快速,正如我們經(jīng)常在一些性能測(cè)試軟件,都會(huì)看到的建議,線程數(shù)最好小于等于CPU核數(shù),最多為CPU核數(shù)的兩倍,這樣壓測(cè)的結(jié)果才是比較準(zhǔn)確的,現(xiàn)在明白了吧。

      最后來看一下大師們總結(jié)的未命中緩存的測(cè)試結(jié)果

      從CPU到 大約需要的 CPU 周期 大約需要的時(shí)間
      主存 約60-80納秒
      QPI 總線傳輸 (between sockets, not drawn) 約20ns
      L3 cache 約40-45 cycles 約15ns
      L2 cache 約10 cycles, 約3ns
      L1 cache 約3-4 cycles 約1ns
      寄存器 寄存器

      每一個(gè)開發(fā)人員都應(yīng)該知道計(jì)算機(jī)硬件IO的延遲數(shù)傳送門

      源碼參考:
      https://github.com/justmine66/Disruptor/blob/master/tests/Disruptor.ConsoleTest/FalseSharingTest.cs

      延伸閱讀

      Magic cache line padding
      The LMAX Architecture

      補(bǔ)充

      感謝@ firstrose同學(xué)主動(dòng)測(cè)試后的提醒,大家應(yīng)該向他學(xué)習(xí),帶著疑惑看博客,不明白的自己動(dòng)手測(cè)試。對(duì)于內(nèi)存布局填充方式,去掉屬性后,經(jīng)過測(cè)試性能與直接填充方式幾乎無差別了,不過本示例代碼僅僅作為一個(gè)測(cè)試參考,主要目的是給大家布道如何利用CPU高速緩存工作機(jī)制,通過緩存行的填充來避免假共享,從而寫出真正低延遲的代碼。

      /// <summary>
      /// CPU高速緩存行條目(控制內(nèi)存布局)
      /// </summary>
      [StructLayout(LayoutKind.Explicit, Size = 120)]
      public class CacheLineEntryOne
      {
          [FieldOffset(56)]
          public long Value;
      }
      

      總結(jié)

      編寫單、多線程下表現(xiàn)都相同的代碼,歷來都是非常困難的,需要不斷地從深度、廣度上積累知識(shí),學(xué)無止境,無癡迷,不成功,希望大家能有所收獲。

      寫在最后

      如果有什么疑問和見解,歡迎評(píng)論區(qū)交流。
      如果你覺得本篇文章對(duì)您有幫助的話,感謝您的【推薦】。
      如果你對(duì).NET高性能編程感興趣的話可以【關(guān)注我】,我會(huì)定期的在博客分享我的學(xué)習(xí)心得。
      歡迎轉(zhuǎn)載,請(qǐng)?jiān)诿黠@位置給出出處及鏈接

      posted @ 2018-09-26 08:16  justmine  閱讀(5014)  評(píng)論(39)    收藏  舉報(bào)
      主站蜘蛛池模板: 无码日韩精品91超碰| 中文字幕亚洲综合第一页| 亚洲精品动漫一区二区三| 亚洲天堂在线免费| 日韩高清亚洲日韩精品一区二区| 国产三级国产精品久久成人| 18禁亚洲一区二区三区| 色av专区无码影音先锋| 九九日本黄色精品视频| 吉川爱美一区二区三区视频| 久久三级国内外久久三级| 国产成AV人片久青草影院| 亚洲性无码av在线| 欧美人人妻人人澡人人尤物 | 国产av一区二区午夜福利| 国产日韩av二区三区| 日韩深夜福利视频在线观看| 十八岁污网站在线观看| 欧美激情一区二区| 日韩人妻少妇一区二区三区 | 国产午夜影视大全免费观看| 国产三级黄色的在线观看| 777久久精品一区二区三区无码| 中国国产免费毛卡片| 亚洲精品成人福利网站| 亚洲三区在线观看无套内射| 成人一区二区三区激情视频| 国产乱妇乱子在线视频| 成人一区二区三区激情视频| 丁香五月亚洲综合深深爱| 国精品无码一区二区三区在线蜜臀| 一区二区三区无码视频免费福利| 亚洲色成人网站www永久四虎| 国产一级精品毛片基地| 在线看av一区二区三区| 国产亚洲情侣一区二区无| 无码国产偷倩在线播放| 亚洲一国产一区二区三区| 成人片黄网站a毛片免费| 国产成人欧美一区二区三区在线| 免费A级毛片中文字幕|