<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
學習到的內容:
1.一個64位元的int型別值,充分利用高32位元和低32位元,進行相關加減以及從一個64位元中拆出高32位元和低32位元.
擴充套件:如何自己實現一個無鎖佇列.
2.記憶體相關操作以及優化
下面是相關原始碼,不過是已經刪減了對本次分析沒有用的程式碼.
type Pool struct { // GMP中,每一個P(協程排程器)會有一個陣列,陣列大小位localSize. local unsafe.Pointer // p 陣列大小. localSize uintptr New func() any } // poolLocal 每個P(協程排程器)的本地pool. type poolLocal struct { poolLocalInternal // 保證一個poolLocal佔用一個快取行 pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte } type poolLocalInternal struct { private any // Can be used only by the respective P. 16 shared poolChain // Local P can pushHead/popHead; any P can popTail. 8 } type poolChain struct { head *poolChainElt tail *poolChainElt } type poolChainElt struct { poolDequeue next, prev *poolChainElt } type poolDequeue struct { // head 高32位元,tail低32位元. headTail uint64 vals []eface } // 儲存具體的value. type eface struct { typ, val unsafe.Pointer }
Put 過程分析比較重要,因為這裡會包含pool所有依賴相關分析.
總的分析學習過程可以分為下面幾個步驟:
1.獲取P
對應的poolLocal
2.val
如何進入poolLocal
下面的poolDequeue
佇列中的.
3.如果當前協程獲取到當前P
對應的poolLocal
之後進行put前,協程讓出CPU使用權,再次排程過來之後,會發生什麼?
4.讀寫記憶體優化.
陣列直接操作記憶體,而不經過Golang
充分利用uint64
值的特性,將head
和tail
用一個值來進行表示,減少CPU存取記憶體次數.
獲取P對應的poolLocal
sync.Pool.local
其實是一個指標,並且通過變數+結構體大小來劃分記憶體空間,從而將這片記憶體直接劃分為陣列. Go 在Put
之前會先對當前Goroutine繫結到當前P中,然後通過pid
獲取其在local
記憶體地址中的歧視指標,在獲取時是會進行記憶體分配的. 具體如下:
func (p *Pool) pin() (*poolLocal, int) { // 返回執行當前協程的P(協程排程器),並且設定禁止搶佔. pid := runtime_procPin() s := runtime_LoadAcquintptr(&p.localSize) // load-acquire l := p.local // load-consume // pid < 核心數. 預設走該邏輯. if uintptr(pid) < s { return indexLocal(l, pid), pid } // 設定的P大於本機CPU核心數. return p.pinSlow() } // indexLocal 獲取當前P的poolLocal指標. func indexLocal(l unsafe.Pointer, i int) *poolLocal { // l p.local指標開始位置. // 我猜測這裡如果l為空,編譯階段會進行優化. lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{})) // uintptr真實的指標. // unsafe.Pointer Go對指標的封裝: 用於指標和結構體互相轉化. return (*poolLocal)(lp) }
從上面程式碼我們可以看到,Go通過runtime_procPin
來設定當前Goroutine獨佔P,並且直接通過頭指標+偏移量(陣列結構體大小)來進行對記憶體劃分為陣列.
Put 進入poolDequeue佇列:
Go在Push時,會通過headtail
來獲取當前佇列內元素個數,如果滿了,則會重新構建一個環型佇列poolChainElt
,並且設定為poolChain.head
,並且賦值next
以及prev
.
通過下面程式碼,我們可以看到,Go通過邏輯運算判斷佇列是否滿的設計時非常巧妙的,如果後續我們去開發元件,也是可以這麼進行設計的。
func (c *poolChain) pushHead(val any) { d := c.head // 初始化. if d == nil { // Initialize the chain. const initSize = 8 // Must be a power of 2 d = new(poolChainElt) d.vals = make([]eface, initSize) c.head = d // 將新構建的d賦值給tail. storePoolChainElt(&c.tail, d) } // 入隊. if d.pushHead(val) { return } // 佇列滿了. newSize := len(d.vals) * 2 if newSize >= dequeueLimit { // 佇列大小預設為2的30次方. newSize = dequeueLimit } // 賦值連結串列前後節點關係. // prev. // d2.prev=d1. // d1.next=d2. d2 := &poolChainElt{prev: d} d2.vals = make([]eface, newSize) c.head = d2 // next . storePoolChainElt(&d.next, d2) d2.pushHead(val) } // 入隊poolDequeue func (d *poolDequeue) pushHead(val any) bool { ptrs := atomic.LoadUint64(&d.headTail) head, tail := d.unpack(ptrs) // head 表示當前有多少元素. if (tail+uint32(len(d.vals)))&(1<<dequeueBits-1) == head { return false } // 環型佇列. head&uint32(len(d.vals)-1) 表示當前元素落的位置一定在佇列上. slot := &d.vals[head&uint32(len(d.vals)-1)] typ := atomic.LoadPointer(&slot.typ) if typ != nil { return false } // The head slot is free, so we own it. if val == nil { val = dequeueNil(nil) } // 向slot寫入指標型別為*any,並且值為val. *(*any)(unsafe.Pointer(slot)) = val // headTail高32位元++ atomic.AddUint64(&d.headTail, 1<<dequeueBits) return true }
Get實現邏輯:
其實我們看了Put
相關邏輯之後,我們可能很自然的就想到了Get
的邏輯,無非就是遍歷連結串列,並且如果佇列中最後一個元素不為空,則會將該元素返回,並且將該插槽賦值為空值.
如何自己實現一個無鎖佇列. 本文未實現,後續文章會進行實現.
橫向思考,並未進行實現,後續會進行實現“
uintptr
和unsafe.Pointer
和結構體指標之間的依賴關係來提升效能.atomic.CompareAndSwapPointer
來設計自旋來達到無鎖化.到此這篇關於深入淺出Golang中的sync.Pool的文章就介紹到這了,更多相關Golang sync.Pool內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45