欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go源碼分析之預(yù)分配slice內(nèi)存

 更新時(shí)間:2023年08月15日 08:49:11   作者:Goland貓  
這篇文章主要從Go語言源碼帶大家分析一下預(yù)分配slice內(nèi)存的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,對(duì)我們深入了解go有一定的幫助,需要的可以學(xué)習(xí)一下

切片擴(kuò)容

在 1.18 版本前,切片擴(kuò)容,在容量小于1024時(shí),以2倍大小擴(kuò)容。超過1024后,以1.25倍擴(kuò)容。在擴(kuò)容后切片的基礎(chǔ)上,會(huì)根據(jù)長(zhǎng)度和容量進(jìn)行 roundupsize 。

在1.18版本后,接下來看一下源碼如下:

func growslice(et *_type, old slice, cap int) slice {
	//  ......
	newcap := old.cap
	doublecap := newcap + newcap //雙倍擴(kuò)容(原容量的兩倍)
	if cap > doublecap {         //如果所需容量大于 兩倍擴(kuò)容,則直接擴(kuò)容到所需容量
		newcap = cap
	} else {
		const threshold = 256    //這里設(shè)置了一個(gè) 閾值 -- 256
		if old.cap < threshold { //如果舊容量 小于 256,則兩倍擴(kuò)容
			newcap = doublecap
		} else {
			// 檢查 0 < newcap 以檢測(cè)溢出并防止無限循環(huán)。
			for 0 < newcap && newcap < cap { //如果新容量 > 0  并且 原容量 小于 所需容量
				// 從小片的增長(zhǎng)2x過渡到大片的增長(zhǎng)1.25x。這個(gè)公式給出了兩者之間的平滑過渡。
				newcap += (newcap + 3*threshold) / 4
				//新容量是 = 1.25 原容量 + 3/4 閾值 (192)
			}
			//當(dāng)newcap計(jì)算溢出時(shí),將newcap設(shè)置為請(qǐng)求的上限。
			if newcap <= 0 { // 如果發(fā)生了溢出,將新容量設(shè)置為請(qǐng)求的容量大小
				newcap = cap
			}
		}
	}
}

函數(shù)判斷如果所需容量 cap 大于兩倍擴(kuò)容的容量 doublecap,說明 cap 的需求已經(jīng)超過了兩倍擴(kuò)容的范圍,所以將 newcap 直接設(shè)為 cap。

否則,如果原容量 old.cap 小于一個(gè)閾值 threshold(這里設(shè)為256),則將 newcap 設(shè)置為原容量的兩倍 doublecap

如果既不滿足上述條件,則進(jìn)入一個(gè)循環(huán),只要 newcap 大于0且小于所需容量 cap,就會(huì)進(jìn)入循環(huán)。在每次循環(huán)迭代中,newcap 的增長(zhǎng)方式為當(dāng)前 newcap 加上 3*threshold 的四分之一。這個(gè)計(jì)算方式使得容量的增長(zhǎng)逐漸從原容量的兩倍過渡到1.25倍,實(shí)現(xiàn)了一個(gè)平滑的過渡。

最后,在循環(huán)結(jié)束后,判斷如果 newcap 仍然小于等于0(溢出情況),則將 newcap 設(shè)為所需容量 cap

如果 現(xiàn)有容量 小于 256 ,則新容量是原來的兩倍

新容量 = 1.25 原容量 + 3/4 閾值 (192) “這個(gè)公式給出了從1.25倍增長(zhǎng) 過渡到2 倍增長(zhǎng),兩者之間的平滑過渡。” 在此情況下,如果發(fā)生了溢出,將新容量設(shè)置為請(qǐng)求的容量大小

代碼測(cè)試

func main() {
	slice := make([]int, 0)
	for i := 0; i < 512; i++ {
		slice = append(slice, i)
	}
	newSlice := append(slice, 5000)
	fmt.Printf("Before Pointer = %p, len = %d, cap = %d\n", &slice, len(slice), cap(slice))
	fmt.Printf("Before Pointer = %p, len = %d, cap = %d\n", &newSlice, len(newSlice), cap(newSlice))
}

執(zhí)行輸出如下:

再看一個(gè)例子

下面用 go1.17 和 go1.18 兩個(gè)版本來分開說明。先通過一段測(cè)試代碼,直觀感受一下兩個(gè)版本在擴(kuò)容上的區(qū)別。

package?main  
import?"fmt"  
func?main()?{  
????s?:=?make([]int,?0)  
????oldCap?:=?cap(s)  
????for?i?:=?0;?i?<?2048;?i++?{  
????????s?=?append(s,?i)  
????????newCap?:=?cap(s)  
????????if?newCap?!=?oldCap?{  
????????????fmt.Printf("[%d?->?%4d]?cap?=?%-4d??|??after?append?%-4d??cap?=?%-4d\n",?0,?i-1,?oldCap,?i,?newCap)  
????????????oldCap?=?newCap  
????????}  
????}  
}

運(yùn)行結(jié)果(1.17 版本):

[0 ->   -1] cap = 0     |  after append 0     cap = 1   
[0 ->    0] cap = 1     |  after append 1     cap = 2   
[0 ->    1] cap = 2     |  after append 2     cap = 4   
[0 ->    3] cap = 4     |  after append 4     cap = 8   
[0 ->    7] cap = 8     |  after append 8     cap = 16  
[0 ->   15] cap = 16    |  after append 16    cap = 32  
[0 ->   31] cap = 32    |  after append 32    cap = 64  
[0 ->   63] cap = 64    |  after append 64    cap = 128 
[0 ->  127] cap = 128   |  after append 128   cap = 256 
[0 ->  255] cap = 256   |  after append 256   cap = 512 
[0 ->  511] cap = 512   |  after append 512   cap = 1024
[0 -> 1023] cap = 1024  |  after append 1024  cap = 1280
[0 -> 1279] cap = 1280  |  after append 1280  cap = 1696
[0 -> 1695] cap = 1696  |  after append 1696  cap = 2304

在分配內(nèi)存空間之前需要先確定新的切片容量,運(yùn)行時(shí)根據(jù)切片的當(dāng)前容量選擇不同的策略進(jìn)行擴(kuò)容:

  • 如果期望容量大于當(dāng)前容量的兩倍就會(huì)使用期望容量;
  • 如果當(dāng)前切片的長(zhǎng)度小于 1024 就會(huì)將容量翻倍;
  • 如果當(dāng)前切片的長(zhǎng)度大于等于 1024 就會(huì)每次增加 25% 的容量,直到新容量大于期望容量;

運(yùn)行結(jié)果(1.18 版本):

[0 ->   -1] cap = 0     |  after append 0     cap = 1  
[0 ->    0] cap = 1     |  after append 1     cap = 2     
[0 ->    1] cap = 2     |  after append 2     cap = 4     
[0 ->    3] cap = 4     |  after append 4     cap = 8     
[0 ->    7] cap = 8     |  after append 8     cap = 16    
[0 ->   15] cap = 16    |  after append 16    cap = 32    
[0 ->   31] cap = 32    |  after append 32    cap = 64    
[0 ->   63] cap = 64    |  after append 64    cap = 128   
[0 ->  127] cap = 128   |  after append 128   cap = 256   
[0 ->  255] cap = 256   |  after append 256   cap = 512   
[0 ->  511] cap = 512   |  after append 512   cap = 848   
[0 ->  847] cap = 848   |  after append 848   cap = 1280  
[0 -> 1279] cap = 1280  |  after append 1280  cap = 1792  
[0 -> 1791] cap = 1792  |  after append 1792  cap = 2560

  • 如果期望容量大于當(dāng)前容量的兩倍就會(huì)使用期望容量;
  • 如果當(dāng)前切片的長(zhǎng)度小于閾值(默認(rèn) 256)就會(huì)將容量翻倍;
  • 如果當(dāng)前切片的長(zhǎng)度大于等于閾值(默認(rèn) 256),就會(huì)每次增加 25% 的容量,基準(zhǔn)是 newcap + 3*threshold,直到新容量大于期望容量;

內(nèi)存對(duì)齊

但是,后半部分還對(duì) newcap 作了一個(gè)內(nèi)存對(duì)齊,這個(gè)和內(nèi)存分配策略相關(guān)。進(jìn)行內(nèi)存對(duì)齊之后,新 slice 的容量是要 大于等于 按照前半部分生成的newcap。

之后,向 Go 內(nèi)存管理器申請(qǐng)內(nèi)存,將老 slice 中的數(shù)據(jù)復(fù)制過去,并且將 append 的元素添加到新的底層數(shù)組中。

最后,向 growslice 函數(shù)調(diào)用者返回一個(gè)新的 slice,這個(gè) slice 的長(zhǎng)度并沒有變化,而容量卻增大了。

測(cè)試代碼

import "fmt"
func main() {
	s := []int{1,2}
	s = append(s,4,5,6)
	fmt.Printf("len=%d, cap=%d",len(s),cap(s))
}

輸出

len=5, cap=6

根據(jù)Go語言中切片的擴(kuò)容機(jī)制,當(dāng)切片容量不足以容納額外的元素時(shí),它會(huì)自動(dòng)進(jìn)行擴(kuò)容。在這種情況下,切片的容量會(huì)根據(jù)需要自動(dòng)增長(zhǎng),通常會(huì)以原來容量的2倍進(jìn)行擴(kuò)容。

根據(jù)您的代碼,初始切片s的容量為2,添加了3個(gè)元素后,長(zhǎng)度變?yōu)?。實(shí)際上,在這種情況下,切片的容量不會(huì)立即擴(kuò)大到8,而是繼續(xù)保持為6。這是因?yàn)镚o語言的切片擴(kuò)容機(jī)制會(huì)優(yōu)化以減少內(nèi)存的浪費(fèi)。擴(kuò)容時(shí),切片會(huì)選擇一個(gè)合適的容量,使得容量盡可能靠近但大于所需的最小容量。

源碼分析

func growslice(et *_type, old slice, cap int) slice {
    // ……
    newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		// ……
	}
	// ……
	capmem = roundupsize(uintptr(newcap) * ptrSize)
	newcap = int(capmem / ptrSize)
}

capmem = roundupsize(uintptr(newcap) * ptrSize):根據(jù) newcap 和指針的大小計(jì)算需要分配的內(nèi)存大小。 newcap = int(capmem / ptrSize):通過將內(nèi)存大小除以指針大小,將新的容量 newcap 轉(zhuǎn)換為整數(shù)。

到此這篇關(guān)于Go源碼分析之預(yù)分配slice內(nèi)存的文章就介紹到這了,更多相關(guān)Go預(yù)分配slice內(nèi)存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文教你如何用好GO語言變長(zhǎng)參數(shù)

    一文教你如何用好GO語言變長(zhǎng)參數(shù)

    對(duì)于函數(shù)重載相信編碼過的?xdm?肯定不會(huì)陌生,那么我們一起分別來看看?C?語言,C++?語言,GO?語言?如何去模擬和使用重載,感興趣的可以學(xué)習(xí)一下
    2023-09-09
  • go語言異常panic和恢復(fù)recover用法實(shí)例

    go語言異常panic和恢復(fù)recover用法實(shí)例

    這篇文章主要介紹了go語言異常panic和恢復(fù)recover用法,實(shí)例分析了異常panic和恢復(fù)recover使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03
  • Go語言如何利用Mutex保障數(shù)據(jù)讀寫正確

    Go語言如何利用Mutex保障數(shù)據(jù)讀寫正確

    這篇文章主要介紹了互斥鎖的實(shí)現(xiàn)機(jī)制,以及?Go?標(biāo)準(zhǔn)庫的互斥鎖?Mutex?的基本使用方法,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下
    2023-05-05
  • Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式

    Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式

    這篇文章主要介紹了Go并發(fā):使用sync.WaitGroup實(shí)現(xiàn)協(xié)程同步方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go返回int64類型字段超出javascript Number范圍的解決方法

    Go返回int64類型字段超出javascript Number范圍的解決方法

    這篇文章主要介紹了Go返回int64類型字段超出javascript Number范圍的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)

    GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)

    這篇文章主要為大家介紹了GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Go語言排序與接口實(shí)例分析

    Go語言排序與接口實(shí)例分析

    這篇文章主要介紹了Go語言排序與接口,實(shí)例分析了排序與接口的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 淺談Go連接池的設(shè)計(jì)與實(shí)現(xiàn)

    淺談Go連接池的設(shè)計(jì)與實(shí)現(xiàn)

    本文主要介紹了淺談Go連接池的設(shè)計(jì)與實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go語言跨平臺(tái)時(shí)字符串中的換行符如何統(tǒng)一?

    Go語言跨平臺(tái)時(shí)字符串中的換行符如何統(tǒng)一?

    本文介紹了Go語言中統(tǒng)一換行符的方法,包括使用`strings.ReplaceAll`函數(shù)將Windows風(fēng)格的換行符`\r\n`替換為Unix風(fēng)格的換行符`\n`,或?qū)\n`替換為`\r\n`,統(tǒng)一換行符可以避免不同平臺(tái)間顯示不一致、傳輸時(shí)出現(xiàn)多余的換行符或丟失換行符,以及解析錯(cuò)誤等問題
    2024-11-11
  • 使用go net實(shí)現(xiàn)簡(jiǎn)單的redis通信協(xié)議

    使用go net實(shí)現(xiàn)簡(jiǎn)單的redis通信協(xié)議

    本文主要介紹了go net實(shí)現(xiàn)簡(jiǎn)單的redis通信協(xié)議,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12

最新評(píng)論