概述
Go 中的空結(jié)構(gòu)體 struct{}{}
的內(nèi)存大小等于 0,除此之外,還有別的數(shù)據(jù)類型內(nèi)存大小也等于 0 嗎?
map 實(shí)現(xiàn) set
Go 的標(biāo)準(zhǔn)庫沒有內(nèi)置的 Set
類型,在不引用第三方包的情況下,一般是結(jié)合內(nèi)置的 map
類型實(shí)現(xiàn) Set
類型相關(guān)功能。
這里假設(shè) Set
元素類型為 int
, 那么我們就以 int
作為 map
的鍵類型,以 bool
作為 map
的值類型 (之所以選擇 bool
類型,是因?yàn)槠浯笮?1 個字節(jié),相對其他數(shù)據(jù)類型可以節(jié)省內(nèi)存,當(dāng)然,也可以使用 byte
類型,其大小同樣是 1 個字節(jié))。
package main
import "fmt"
// Set 類型定義
type set map[int]bool
// 初始化一個新的 Set
func newSet() set {
return make(set)
}
// 元素是否存在于與集合中
func (s set) contains(ele int) bool {
_, ok := s[ele]
return ok
}
// 添加元素到集合
func (s set) add(ele int) {
s[ele] = true
}
// 從集合中刪除某個元素
func (s set) remove(ele int) {
delete(s, ele)
}
func main() {
s := newSet()
fmt.Println(s.contains(100))
s.add(100)
fmt.Println(s.contains(100))
s.remove(100)
fmt.Println(s.contains(100))
}
$ go run main.go
# 輸出如下
false
true
false
上述示例代碼通過內(nèi)置類型 map
實(shí)現(xiàn)了 set
類型相關(guān)功能,美中不足的一點(diǎn)在于: 每個元素都要浪費(fèi)一個 bool
類型作為標(biāo)識占位符, 能否進(jìn)一步的優(yōu)化呢 ?當(dāng)然是可以的,這就是接下來要講到的 空結(jié)構(gòu)體
了。
空結(jié)構(gòu)體
Go 中的空結(jié)構(gòu)體 struct{}{}
是 一個底層的內(nèi)置變量,不占用任何內(nèi)存 :
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Printf("size = %d\\n", unsafe.Sizeof(struct{}{}))
}
$ go run main.go
# 輸出如下
size = 0
結(jié)合剛才的例子,可以將 struct{}{}
作為 Set
的元素,這樣不論 Set
有多少個元素,標(biāo)志位
內(nèi)存占用始終為 0 。
使用 bool 實(shí)現(xiàn) Set
測試代碼
package performance
import (
"testing"
)
// Set 類型定義, 使用 bool 類型作為占位符
type set map[int]bool
// 初始化一個新的 Set
func newSet() set {
return make(set)
}
// 元素是否存在于與集合中
func (s set) contains(ele int) bool {
_, ok := s[ele]
return ok
}
// 添加元素到集合
func (s set) add(ele int) {
s[ele] = true
}
// 從集合中刪除某個元素
func (s set) remove(ele int) {
delete(s, ele)
}
func Benchmark_Compare(b *testing.B) {
s := newSet()
for i := 0; i < b.N; i++ {
s.add(i)
}
for i := 0; i < b.N; i++ {
_ = s.contains(i)
s.remove(i)
}
}
運(yùn)行測試,并將基準(zhǔn)測試結(jié)果寫入文件:
$ go test -run='^$' -bench=. -count=1 -benchtime=10000000x -benchmem > bool.txt
使用空結(jié)構(gòu)體實(shí)現(xiàn) Set
測試代碼
package performance
import (
"testing"
)
// Set 類型定義, 使用 bool 類型作為占位符
type set map[int]struct{}
// 初始化一個新的 Set
func newSet() set {
return make(set)
}
// 元素是否存在于與集合中
func (s set) contains(ele int) bool {
_, ok := s[ele]
return ok
}
// 添加元素到集合
func (s set) add(ele int) {
s[ele] = struct{}{}
}
// 從集合中刪除某個元素
func (s set) remove(ele int) {
delete(s, ele)
}
func Benchmark_Compare(b *testing.B) {
s := newSet()
for i := 0; i < b.N; i++ {
s.add(i)
}
for i := 0; i < b.N; i++ {
_ = s.contains(i)
s.remove(i)
}
}
運(yùn)行測試,并將基準(zhǔn)測試結(jié)果寫入文件:
$ go test -run='^$' -bench=. -count=1 -benchtime=10000000x -benchmem > struct.txt
使用 benchstat 比較差異
$ benchstat -alpha=100 bool.txt struct.txt
# 輸出如下
name old time/op new time/op delta
_Compare-8 371ns ± 0% 332ns ± 0% -10.47% (p=1.000 n=1+1)
name old alloc/op new alloc/op delta
_Compare-8 44.0B ± 0% 40.0B ± 0% -9.09% (p=1.000 n=1+1)
name old allocs/op new allocs/op delta
_Compare-8 0.00 0.00 ~ (all equal)
從輸出的結(jié)果中可以看到,相比于使用 bool
作為 Set
元素占位符,使用 空結(jié)構(gòu)體
在性能和內(nèi)存占用方面,都有了小幅度的優(yōu)化提升 (10% 左右)。因?yàn)闀r間關(guān)系,這里的基準(zhǔn)測試只運(yùn)行了 10000000 次, 運(yùn)行次數(shù)越大,優(yōu)化的效果越明顯 。感興趣的讀者可以將 -benchtime
調(diào)大后看看優(yōu)化效果。
小結(jié)
**Go 中的空結(jié)構(gòu)體 **struct{}{}
不占用任何內(nèi)存,而且有很清晰的語義性質(zhì) (作為占位符使用) 。除了剛才示例中實(shí)現(xiàn) Set
類型功能外, 還可以使用空結(jié)構(gòu)體作為 通道信號標(biāo)識
、空對象
等,各種使用場景請讀者自行探索。
彩蛋
除了空結(jié)構(gòu)體 struct{}{}
之外,還有一個鮮為人知的內(nèi)存大小為 0 的數(shù)據(jù)類型是: 空數(shù)組
。但是相對 struct{}{}
豐富的表達(dá)性,空數(shù)組
使用的場景很少。
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Printf("size = %d\\n", unsafe.Sizeof([0]int{}))
}
$ go run main.go
# 輸出如下
size = 0
-
go語言
+關(guān)注
關(guān)注
1文章
159瀏覽量
9324
發(fā)布評論請先 登錄
LabVIEW的數(shù)據(jù)類型、存儲格式
LabVIEW的數(shù)據(jù)類型、存儲格式
Python中常用的數(shù)據(jù)類型
STM32F103xx系列內(nèi)存大小
電腦內(nèi)存大小怎么查看
c++ 之布爾類型和引用的學(xué)習(xí)總結(jié)
重視變量的數(shù)據(jù)類型

C語言中內(nèi)存四區(qū)模型的本質(zhì)區(qū)別和代碼分析
什么是數(shù)據(jù)類型轉(zhuǎn)換
CODESYS變量類型的范圍和所占內(nèi)存大小

Verilog最常用的2種數(shù)據(jù)類型

評論