2017-05-30 67 views
1

我很好奇mapslice的內存成本,所以我寫了一個程序來比較大小。我得到的內存大小爲unsafe.Sizeof(s),但顯然這是錯誤的,因爲當我改變大小時,輸出是相同的。如何在golang中獲取變量的變量內存大小?

func getSlice(size int) []int { 
    t := time.Now() 
    s := make([]int, size*2) 
    for i := 0; i < size; i++ { 
     index := i << 1 
     s[index] = i 
     s[index+1] = i 
    } 
    fmt.Println("slice time cost: ", time.Since(t)) 
    return s 
} 

func getMap(size int) map[int]int { 
    t := time.Now() 
    m := make(map[int]int, size) 
    for i := 0; i < size; i++ { 
     m[i] = i 
    } 
    fmt.Println("map time cost: ", time.Since(t)) 
    return m 
} 

func TestMem(t *testing.T) { 
    size := 1000 
    s := getSlice(size) 
    m := getMap(size) 
    fmt.Printf("slice size: %d\n", unsafe.Sizeof(s)) 
    fmt.Printf("map size: %d\n", unsafe.Sizeof(m)) 
} 
+0

沒有_need_這樣做,因爲你可以計算所需的空間:切片的大小乘以每個元素的大小。對於地圖而言,由於某些內部區域無法訪問(例如散列衝突)而變得更加困難,但它基本上是相同的。 – Volker

回答

3

unsafe.SizeOf()reflect.Type.Size()只返回傳遞值的大小,不遞歸遍歷數據結構並添加指向值的大小。

切片是相對簡單的結構:reflect.SliceHeader,因爲我們知道它引用的背襯陣列,我們可以很容易地計算其大小「手動」,例如:

s := make([]int32, 1000) 

fmt.Println("Size of []int32:", unsafe.Sizeof(s)) 
fmt.Println("Size of [1000]int32:", unsafe.Sizeof([1000]int32{})) 
fmt.Println("Real size of s:", unsafe.Sizeof(s)+unsafe.Sizeof([1000]int32{})) 

輸出(嘗試在Go Playground ):

Size of []int32: 12 
Size of [1000]int32: 4000 
Real size of s: 4012 

地圖有很多更復雜的數據結構,我就不贅述了,但檢查出這個問題的答案+:Golang: computing the memory footprint (or byte length) of a map

如果你想要「真實」的數字,你可以利用Go的測試工具,它也可以執行內存基準測試。傳遞-benchmem參數,基準函數內部僅要衡量其內存分配:(從getSlice()當然getMap()拆下正和打印話費)

func BenchmarkSlice100(b *testing.B) { 
    for i := 0; i < b.N; i++ { getSlice(100) } 
} 
func BenchmarkSlice1000(b *testing.B) { 
    for i := 0; i < b.N; i++ { getSlice(1000) } 
} 
func BenchmarkSlice10000(b *testing.B) { 
    for i := 0; i < b.N; i++ { getSlice(10000) } 
} 
func BenchmarkMap100(b *testing.B) { 
    for i := 0; i < b.N; i++ { getMap(100) } 
} 
func BenchmarkMap1000(b *testing.B) { 
    for i := 0; i < b.N; i++ { getMap(1000) } 
} 
func BenchmarkMap10000(b *testing.B) { 
    for i := 0; i < b.N; i++ { getMap(10000) } 
} 

運行
go test -bench . -benchmem 

輸出是:

BenchmarkSlice100-4 3000000  471 ns/op  1792 B/op  1 allocs/op 
BenchmarkSlice1000-4 300000  3944 ns/op  16384 B/op  1 allocs/op 
BenchmarkSlice10000-4 50000  39293 ns/op  163840 B/op  1 allocs/op 
BenchmarkMap100-4  200000  11651 ns/op  2843 B/op  9 allocs/op 
BenchmarkMap1000-4  10000  111040 ns/op  41823 B/op  12 allocs/op 
BenchmarkMap10000-4  1000 1152011 ns/op  315450 B/op 135 allocs/op 

B/op值告訴你每個操作分配了多少個字節。 allocs/op表示每個操作有多少(不同的)內存分配。

在我的64位體系結構(其中int的大小是8個字節)中,它指出具有2000個元素的片大小大約爲16 KB(與2000 * 8字節一致)。具有1000個​​對的地圖大約需要分配42 KB。

0

這是正確的方法,使用unsafe.Sizeof(s)。只是對於給定類型(整數,字符串等),結果將保持不變,而不考慮確切的值。

Sizeof需要任何類型的表達式x並返回假設變量v的字節大小,就像v通過var v = x聲明一樣。該大小不包括可能由x引用的任何內存。例如,如果x是切片,則Sizeof返回切片描述符的大小,而不是切片引用的內存大小。

參考here

更新:

您可以使用編組,然後以字節Size()比較值表示。這只是將數據轉換爲字節字符串的問題。

+0

因此,如何才能在我的情況下獲得真實的大小? – roger