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

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

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

      atomic不是免費午餐

      很多初級甚至中級開發會濫用atomic,因為在他們的世界觀里atomic比mutex輕量,性能總是優于鎖的。

      這話不能算錯,但有個很重要的前提,那就是原子操作競爭不激烈的時候。

      “競爭激烈”是指什么呢,指的是有很多線程在同一個資源上大量執行原子操作的情況。

      落在這種情況下原子操作反而會成為性能拖油瓶。我們來看一個經典的原子計數器:

      func AddAtomic() uint64 {
      	var count atomic.Uint64
      	var wg sync.WaitGroup
      	for range 10 {
      		wg.Add(1)
      		go func() {
      			for range 100000000 {
      				count.Add(1)
      			}
      			wg.Done()
      		}()
      	}
      	wg.Wait()
      	return count.Load()
      }
      

      代碼模擬了10個線程頻繁操作計數器的場景。論并發安全這段代碼是既簡潔又安全的。很多人可能還會覺得這段代碼是很高效的,畢竟用了原子操作嘛。

      不過別著急,測試性能之前我們再想想還有沒有其他做法。考慮到這是一個單向遞增的計數器,我們只需要保證每次的加操作最終都能完成,并且因為加法的交換律和結合律,這些操作的相對順序也可以打亂。換句話說,我們可以不關心counter的中間狀態,每個線程自己聚合所有的加操作,最后再一次性加給counter。代碼就會變成下面這樣:

      func AddAtomicLocal() uint64 {
      	var count atomic.Uint64
      	var wg sync.WaitGroup
      	for range 10 {
      		wg.Add(1)
      		go func() {
      			cc := uint64(0)
      			for range 100000000 {
      				cc++
      			}
      			count.Add(cc)
      			wg.Done()
      		}()
      	}
      	wg.Wait()
      	return count.Load()
      }
      

      現在每個線程維護自己的計數器,在運行結束統一操作counter。熟悉Java的讀者應該能看出來這是LongAdder,唯一的區別是我們沒用threadlocal。

      兩種方法累加的次數都是一樣的,而且大部分人都認為原子操作很輕量,那么它們的性能理論上應該不會差太多,方法1稍微慢一點。我們寫點性能測試看下:

      func BenchmarkAtomic(b *testing.B) {
      	for b.Loop() {
      		result := AddAtomic()
      		if result != 1000000000 {
      			b.Fatal("error")
      		}
      	}
      }
      
      func BenchmarkAtomicLocal(b *testing.B) {
      	for b.Loop() {
      		result := AddAtomicLocal()
      		if result != 1000000000 {
      			b.Fatal("error")
      		}
      	}
      }
      

      下面是測試結果,分別用go1.24.5在Intel和Apple的機器上進行測試:

      Snipaste_2025-08-02_16-23-39

      Snipaste_2025-08-01_17-16-28

      結果出人意料,在兩款不同的芯片上,方案1都慢了接近300倍。看似“輕量”的原子操作竟然是性能殺手。

      我們可以找個Linux系統用perf分析一下原因。在Linux上兩者直接也有百倍的差距。

      上面一個樣本是方案1的,下面的則是方案2的:

      bad

      thegood

      我沒有監聽全部的性能事件,那樣一來會讓程序變得很慢,二來對我們重要的只有其中的幾個事件,太多的信息會成為雜音。

      我們先來看branchesbranch-misses,前者是程序運行中一個執行多少分支判斷,后者是cpu預執行分支失敗的次數,簡單地說misses越少程序性能越高。所以兩個方案在分支總數差不多的情況下方案2比1的預測失敗數量低20倍,所以方案2在這一點上勝出。

      接著就是緩存命中率了,這一點無需過多解釋,命中率越高性能越好。方案二同樣比一高了10%。

      然而這兩點只能解釋一個數量級的差異,但我們現在的差距是300倍。

      仔細觀察緩存讀取次數,我們會驚奇地發現方案1的讀取次數是10,029,023,298,100億次。我們的測試程序也正好運行了累加器函數10次,也是100億次操作。這是方案2的整整18000倍。

      為什么會有這個結果呢?這就是原子操作的缺點之一了:x86和arm上的原子操作都是針對某塊內存上的數據進行操作。這意味著不管是原子讀還是原子寫,都要直接操作內存。現代cpu不會自己直接接觸內存,都需要數據先進入cpu的高速緩存才能進行操作。這就是為什么方案1會有如此之高的緩存讀取次數。原子操作需要這樣的代價,因為共享的資源隨時會被修改,因此只能每次從內存中存取最新的數據。

      而方案2的累加操作是在線程獨立的本地局部變量上進行的,這些操作沒必要走內存,可以直接在寄存器上完成。

      寄存器和高速緩存的速度差異不同體系結構和廠家的產品大相徑庭,但寄存器的速度一定大于等于高速緩存。因此更依賴寄存器的方案2自然會比緩存命中率更低且需要大量操作緩存的方案1快上兩個數量級。

      除此之外原子操作還帶來了另外的副作用,這在perf的報告中沒有顯現——多個線程頻繁修改同一個資源,會帶來大量的更新cpu緩存的核間通信以及線程為了原子性可能會出現很多同步操作。核間通信本身有延遲,緩存狀態更新后cpu遇到下次的原子讀取/寫入就得先更新緩存才能執行操作,一來一去慢的可不是一星半點。線程之間的同步則來自于原子操作帶來的內存屏障,cpu為了保證能讓屏障正常生效,需要讓一些cpu核心上的指令等待另一些核心上的指令執行完成,不同的cpu實現這點的方法不同,但也會帶來可觀的延遲。

      所以綜合上面三個原因,看似輕量級的原子操作在“競爭激烈”的場景下出現了嚴重的性能問題。

      不過盡管方案2很快,但它也有缺點,那就是counter不會及時更新。在這里我們可以忍受這一點,但也有的場景是無法接受這種延遲的。

      總結

      atomic不是免費午餐,是要支付使用代價的。除了潛在的性能問題,還會有難以察覺的并發問題。

      所以為了追求性能,應該停止濫用atomic。可以適當地像方案2或者Java的LongAdder那樣選擇一些per-cpu的算法或者數據結構。

      posted @ 2025-08-03 21:50  apocelipes  閱讀(544)  評論(9)    收藏  舉報
      主站蜘蛛池模板: 达州市| 欧美成本人视频免费播放| 久久成人影院精品777| 狠狠v日韩v欧美v| 人妻中文字幕一区二区视频| 日本夜爽爽一区二区三区| 日韩乱码人妻无码中文字幕视频| 性色av一区二区三区精品| 亚洲成A人片在线观看无码不卡 | 光棍天堂在线手机播放免费| 韩国无码av片在线观看| 少妇人妻偷人精品视蜜桃| 国产色无码专区在线观看| 国产精品一区二区久久毛片| 老熟妇乱子交视频一区| 麻豆蜜桃av蜜臀av色欲av| 噜噜噜噜私人影院| 瑞金市| 精品人妻午夜一区二区三区四区| 国产精品第一页中文字幕| 国产精品v欧美精品∨日韩| 人与禽交av在线播放| 少妇精品视频一码二码三| 欧美人禽杂交狂配| 精品亚洲一区二区三区在线观看| AV最新高清无码专区| 福利一区二区在线播放| 99精品国产精品一区二区| 欧美精品高清在线观看| 亚洲岛国成人免费av| 国产伦码精品一区二区| 玩弄放荡人妻少妇系列| 国产精品白丝一区二区三区| 国产成人AV男人的天堂| 亚洲天堂一区二区三区四区| 东京热大乱系列无码| 亚洲精品乱码久久久久久不卡| 人人爽人人爽人人片av东京热 | 亚洲AV无码国产在丝袜APP| 国产精品视频亚洲二区| 午夜免费啪视频|