最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言中節(jié)省內存技巧方法示例

 更新時間:2023年01月06日 14:24:45   作者:nil  
這篇文章主要為大家介紹了Go語言中節(jié)省內存技巧方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

GO雖然不消耗大量內存,但是仍有一些小技巧可以節(jié)省內存,良好的編碼習慣是每一個程序員都應該具備的素質。

預先分配切片

數(shù)組是具有連續(xù)內存的相同類型的集合。數(shù)組類型定義時要指定長度和元素類型。

因為數(shù)組的長度是它們類型的一部分,數(shù)組的主要問題是它們大小固定,不能調整。

與數(shù)組類型不同,切片類型無需指定長度。切片的聲明方式與數(shù)組相同,但沒有數(shù)量元素。

切片是數(shù)組的包裝器,它們不擁有任何數(shù)據(jù)——它們是對數(shù)組的引用。它們由指向數(shù)組的指針、長度及其容量(底層數(shù)組中的元素數(shù))組成。

當您向沒有足夠容量的切片添加一個新值時 - 會創(chuàng)建一個具有更大容量的新數(shù)組,并將當前數(shù)組中的值復制到新數(shù)組中。這會導致不必要的內存分配和 CPU 周期。

為了更好地理解這一點,讓我們看一下以下代碼段:

func main() {
	var ints []int
	fmt.Printf("Address: %p, Length: %d, Capacity: %d, Values: %v\n", ints, len(ints), cap(ints), ints)
	for i := 0; i < 5; i++ {
		ints = append(ints, i)
		fmt.Printf("Address: %p, Length: %d, Capacity: %d, Values: %v\n", ints, len(ints), cap(ints), ints)
	}
}

結果

Address: 0x0, Length: 0, Capacity: 0, Values: []
Address: 0xc0000160d0, Length: 1, Capacity: 1, Values: [0]
Address: 0xc0000160e0, Length: 2, Capacity: 2, Values: [0 1]
Address: 0xc000020100, Length: 3, Capacity: 4, Values: [0 1 2]
Address: 0xc000020100, Length: 4, Capacity: 4, Values: [0 1 2 3]
Address: 0xc00001a180, Length: 5, Capacity: 8, Values: [0 1 2 3 4]

可以看到第一次聲明數(shù)組var ints []int的時候,是不給它分配內存的,內存地址為0,大小和容量也都是0 后面每次擴容都是2的倍數(shù),并且每次擴容內存地址都發(fā)生了改變。

當容量<1024 時會漲為之前的 2 倍,當容量>=1024時會以 1.25 倍增長。從 Go 1.18 開始,這已經(jīng)變得更加線性

func BenchmarkPreallocAssign(b *testing.B) {
	ints := make([]int, b.N)
	for i := 0; i < b.N; i++ {
		ints[i] = i
	}
}
func BenchmarkAppend(b *testing.B) {
	var ints []int
	for i := 0; i < b.N; i++ {
		ints = append(ints, i)
	}
}

結果如下

goos: darwin
goarch: amd64
pkg: mygo
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkPreallocAssign-12      321257311                3.609 ns/op           8 B/op          0 allocs/op
BenchmarkAppend-12              183322678               12.37 ns/op           42 B/op          0 allocs/op
PASS
ok      mygo    6.236s

由上述基準,我們可以得出結論,將值分配給預分配的切片和將值追加到切片之間是存在很大差異的。預先分配大小可以提速3倍多,而且內存分配也更小。

結構體中的字段順序

以下面結構體為例

type Post struct {
    IsDraft     bool      // 1 byte
    Title       string    // 16 bytes
    ID          int64     // 8 bytes
    Description string    // 16 bytes
    IsDeleted   bool      // 1 byte
    Author      string    // 16 bytes
    CreatedAt   time.Time // 24 bytes
}
func main(){
    p := Post{}
    fmt.Println(unsafe.Sizeof(p))
}

上述的輸出為 96 字節(jié),而所有字段相加為 82 字節(jié)。那額外的 14 個字節(jié)是來自哪里呢?

現(xiàn)代 64 位 CPU 以 64 位(8 字節(jié))的塊獲取數(shù)據(jù)

第一個周期占用 8 個字節(jié),拉取“IsDraft”字段占用了 1 個字節(jié)并且產(chǎn)生 7 個未使用字節(jié)。它不能占用“一半”的字段。

第二個和第三個周期取 Title 字符串,第四個周期取 ID,依此類推。到取 IsDeleted 字段時,它使用 1 個字節(jié)并有 7 個字節(jié)未使用。

對內存節(jié)省的關鍵是按字段占用大小從上到下對字段進行排序。對上述結構進行排序,大小可減少到 88 個字節(jié)。最后兩個字段 IsDraft 和 IsDeleted 被放在同一個塊中,從而將未使用的字節(jié)數(shù)從 14 (2x7) 減少到 6 (1 x 6),在此過程中節(jié)省了 8 個字節(jié)。

type Post struct {
    CreatedAt   time.Time // 24 bytes
    Title       string    // 16 bytes
    Description string    // 16 bytes
    Author      string    // 16 bytes
    ID          int64     // 8 bytes
    IsDraft     bool      // 1 byte
    IsDeleted   bool      // 1 byte
}
func main(){
    p := Post{}
    fmt.Println(unsafe.Sizeof(p))
}

上述的輸出為 88 字節(jié)

極端情況

type Post struct {
	IsDraft  bool  // 1 byte
	I64      int64 // 8 bytes
	IsDraft1 bool  // 1 byte
	I641     int64 // 8 bytes
	IsDraft2 bool  // 1 byte
	I642     int64 // 8 bytes
	IsDraft3 bool  // 1 byte
	I643     int64 // 8 bytes
	IsDraft4 bool  // 1 byte
	I644     int64 // 8 bytes
	IsDraft5 bool  // 1 byte
	I645     int64 // 8 bytes
	IsDraft6 bool  // 1 byte
	I646     int64 // 8 bytes
	IsDraft7 bool  // 1 byte
	I647     int64 // 8 bytes
}
type Post1 struct {
	IsDraft  bool  // 1 byte
	IsDraft1 bool  // 1 byte
	IsDraft2 bool  // 1 byte
	IsDraft3 bool  // 1 byte
	IsDraft4 bool  // 1 byte
	IsDraft5 bool  // 1 byte
	IsDraft6 bool  // 1 byte
	IsDraft7 bool  // 1 byte
	I64      int64 // 8 bytes
	I641     int64 // 8 bytes
	I642     int64 // 8 bytes
	I643     int64 // 8 bytes
	I644     int64 // 8 bytes
	I645     int64 // 8 bytes
	I646     int64 // 8 bytes
	I647     int64 // 8 bytes
}

第一個結構體占用128字節(jié),第二個結構體占用72字節(jié)。節(jié)省空間:(128-72)/129=43.75%.

在 64 位架構上占用小于 8 字節(jié)的 Go 類型:

  • bool: 1 個字節(jié)
  • int8/uint8: 1 個字節(jié)
  • int16/uint16: 2 個字節(jié)
  • int32/uint32/rune: 4 個字節(jié)
  • float32: 4 個字節(jié)
  • byte: 1 個字節(jié)

使用 map[string]struct{} 而不是 map[string]bool

Go 沒有內置的集合,通常使用 map[string]bool{} 表示集合。盡管它更具可讀性(這非常重要),但將其作為一個集合使用是錯誤的,因為它具有兩種狀態(tài)(假/真)并且與空結構體相比使用了額外的內存。

空結構體 (struct{}) 是沒有額外字段的結構類型,占用零字節(jié)的存儲空間。

func BenchmarkBool(b *testing.B) {
	m := make(map[uint]bool)
	for i := uint(0); i < 100_000_000; i++ {
		m[i] = true
	}
}
func BenchmarkEmptyStruct(b *testing.B) {
	m := make(map[uint]struct{})
	for i := uint(0); i < 100_000_000; i++ {
		m[i] = struct{}{}
	}
}

結果

goos: darwin
goarch: amd64
pkg: mygo
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkBool-12                       1        24052439603 ns/op       3766222824 B/op  3902813 allocs/op
BenchmarkEmptyStruct-12                1        22450213018 ns/op       3418648448 B/op  3903556 allocs/op
PASS
ok      mygo    46.937s

可以看到執(zhí)行速度提升了一些,但是效果不太明顯。

使用bool值有個好處是查找的時候更方便,從map中取值只需要判斷一個值就行了,而使用空結構體則需要判斷第二個值

m := make(map[string]bool{})
if m["key"]{
 // Do something
}
v := make(map[string]struct{}{})
if _, ok := v["key"]; ok{
    // Do something
}

參考

【1】Go 中簡單的內存節(jié)省技巧

【2】Easy memory-saving tricks in Go

以上就是Go語言中節(jié)省內存技巧方法示例的詳細內容,更多關于Go語言節(jié)省內存技巧的資料請關注腳本之家其它相關文章!

相關文章

  • Go語言實現(xiàn)配置熱加載的方法分享

    Go語言實現(xiàn)配置熱加載的方法分享

    web項目,經(jīng)常需要熱啟動各種各樣的配置信息,一旦這些服務發(fā)生變更,我們需要重新啟動web server,以使配置生效,實現(xiàn)配置熱加載,本文為大家整理了幾個方法實現(xiàn)這個需求,需要的可以參考下
    2023-05-05
  • Go語言學習教程之goroutine和通道的示例詳解

    Go語言學習教程之goroutine和通道的示例詳解

    這篇文章主要通過A?Tour?of?Go中的例子進行學習,以此了解Go語言中的goroutine和通道,文中的示例代碼講解詳細,感興趣的可以了解一下
    2022-09-09
  • 夯實Golang基礎之數(shù)據(jù)類型梳理匯總

    夯實Golang基礎之數(shù)據(jù)類型梳理匯總

    這篇文章主要8為大家介紹了夯實Golang基礎之數(shù)據(jù)類型梳理匯總,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2023-10-10
  • 使用go操作redis的有序集合(zset)

    使用go操作redis的有序集合(zset)

    這篇文章主要介紹了使用go操作redis的有序集合(zset),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang環(huán)境搭建及打包和工具鏈詳解

    Golang環(huán)境搭建及打包和工具鏈詳解

    這篇文章主要介紹了Golang環(huán)境搭建及打包和工具鏈,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • golang實現(xiàn)并發(fā)控制的方法和技巧

    golang實現(xiàn)并發(fā)控制的方法和技巧

    golang 是一門支持并發(fā)的編程語言,它提供了 goroutine 和 channel 等強大的特性,讓我們可以輕松地創(chuàng)建和管理多個執(zhí)行單元,實現(xiàn)高效的任務處理,在本文中,我們將介紹一些 golang 的并發(fā)控制的方法和技巧,希望對你有所幫助
    2024-03-03
  • zap接收gin框架默認的日志并配置日志歸檔示例

    zap接收gin框架默認的日志并配置日志歸檔示例

    本文介紹了在基于gin框架開發(fā)的項目中如何配置并使用zap來接收并記錄gin框架默認的日志和如何配置日志歸檔。有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-04-04
  • 對Golang中的runtime.Caller使用說明

    對Golang中的runtime.Caller使用說明

    這篇文章主要介紹了對Golang中的runtime.Caller使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang基于泛化調用與Nacos實現(xiàn)Dubbo代理

    Golang基于泛化調用與Nacos實現(xiàn)Dubbo代理

    這篇文章主要為大家詳細介紹了Golang如何基于泛化調用與Nacos實現(xiàn)Dubbo代理,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-04-04
  • GO workPool的線程池實現(xiàn)

    GO workPool的線程池實現(xiàn)

    本文主要介紹了GO workPool的線程池實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03

最新評論

翁牛特旗| 凤冈县| 乐清市| 临安市| 德兴市| 北安市| 苍山县| 田阳县| 西平县| 察哈| 拉孜县| 定襄县| 洪泽县| 武邑县| 健康| 福安市| 绥德县| 砚山县| 甘洛县| 蓬莱市| 沧州市| 林甸县| 句容市| 通化县| 灵台县| 灌云县| 潞西市| 夏津县| 邳州市| 新郑市| 沈阳市| 德安县| 石泉县| 图木舒克市| 津市市| 开鲁县| 揭东县| 泸定县| 仁寿县| 和林格尔县| 阿拉善右旗|