web-dev-qa-db-ja.com

Goで変数のメモリサイズを取得するにはどうすればよいですか?

mapsliceのメモリコストに興味があるので、サイズを比較するプログラムを作成しました。 unsafe.Sizeof(s)でメモリサイズを取得しますが、サイズを変更しても出力が同じであるため、明らかに間違っています。

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))
}
8
roger

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:マップのメモリフットプリント(またはバイト長)の計算

変数または構造のサイズを(再帰的に)計算する

「実際の」数値が必要な場合は、メモリのベンチマークも実行できるGoのテストツールを利用できます。 _-benchmem_引数を渡し、ベンチマーク関数内で、測定するメモリのみを割り当てます。

_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) }
}
_

(もちろん、getSlice()getMap()からタイミングと印刷の呼び出しを削除します。)

で実行

_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 _int-int_ペアのマップでは、約42KBを割り当てる必要があります。

11
icza

これは、unsafe.Sizeof(s)を使用した正しい方法です。正確な値を無視して、整数、文字列など、特定の型の結果が同じままになるだけです。

Sizeofは、任意のタイプの式xを受け取り、vがvar v = xを介して宣言されたかのように、仮想変数vのサイズをバイト単位で返します。サイズには、xによって参照される可能性のあるメモリは含まれていません。たとえば、xがスライスの場合、Sizeofは、スライスによって参照されるメモリのサイズではなく、スライス記述子のサイズを返します。

参照 ここ

更新:

マーシャリングを使用して、バイト単位の値表現をSize()と比較できます。データをバイト文字列に変換するだけです。

0
Damaged Organic