How to get memory size of variable in Go?

huangapple go评论91阅读模式
英文:

How to get memory size of variable in 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))
}
英文:

I am curious about the memory cost of map and slice, so I wrote a program to compare the sizes. I get the memory size by unsafe.Sizeof(s), but obviously it is wrong, because when I change the size, the output is the same.

func getSlice(size int) []int {
	t := time.Now()
	s := make([]int, size*2)
	for i := 0; i &lt; size; i++ {
		index := i &lt;&lt; 1
		s[index] = i
		s[index+1] = i
	}
	fmt.Println(&quot;slice time cost: &quot;, 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 &lt; size; i++ {
		m[i] = i
	}
	fmt.Println(&quot;map time cost: &quot;, time.Since(t))
	return m
}

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

答案1

得分: 31

unsafe.SizeOf()reflect.Type.Size()只返回传递值的大小,而不会递归遍历数据结构并添加指向值的大小。

切片是一个相对简单的结构:reflect.SliceHeader,由于我们知道它引用了一个支持数组,我们可以很容易地“手动”计算它的大小,例如:

s := make([]int32, 1000)

fmt.Println("[]int32的大小:", unsafe.Sizeof(s))
fmt.Println("[1000]int32的大小:", unsafe.Sizeof([1000]int32{}))
fmt.Println("s的实际大小:", unsafe.Sizeof(s)+unsafe.Sizeof([1000]int32{}))

输出结果(在Go Playground上尝试):

[]int32的大小:12
[1000]int32的大小:4000
s的实际大小:4012

映射是更复杂的数据结构,我不会详细介绍,但可以查看这个问题+答案:https://stackoverflow.com/questions/31847549/golang-computing-the-memory-footprint-or-byte-length-of-a-map

计算任何变量或结构的大小(递归)

如果你想要“真实”的数字,你可以利用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对的映射大约需要分配42 KB。

英文:

unsafe.SizeOf() and reflect.Type.Size() only return the size of the passed value without recursively traversing the data structure and adding sizes of pointed values.

The slice is relatively a simple struct: reflect.SliceHeader, and since we know it references a backing array, we can easily compute its size "manually", e.g.:

s := make([]int32, 1000)

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

Output (try it on the Go Playground):

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

Maps are a lot more complex data structures, I won't go into details, but check out this question+answer: https://stackoverflow.com/questions/31847549/golang-computing-the-memory-footprint-or-byte-length-of-a-map

Calculating size of any variable or structure (recursively)

If you want "real" numbers, you may take advantage of the testing tool of Go, which can also perform memory benchmarking. Pass the -benchmem argument, and inside the benchmark function allocate only whose memory you want to measure:

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

(Remove the timing and printing calls from getSlice() and getMap() of course.)

Running with

go test -bench . -benchmem

Output is:

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 values tell you how many bytes were allocated per op. allocs/op tells how many (distinct) memory allocations occurred per op.

On my 64-bit architecture (where the size of int is 8 bytes) it tells that the size of a slice having 2000 elements is roughly 16 KB (in line with 2000 * 8 bytes). A map with 1000 int-int pairs required approximately to allocate 42 KB.

答案2

得分: 16

这会产生一些编组开销,但我发现这是在运行时获取Go语言值大小的最简单方法。对于我的需求来说,编组开销并不是一个大问题,所以我选择了这种方法。

func getRealSizeOf(v interface{}) (int, error) {
	b := new(bytes.Buffer)
	if err := gob.NewEncoder(b).Encode(v); err != nil {
		return 0, err
	}
	return b.Len(), nil
}
英文:

This incurs some marshaling overhead but I've found it's the simplest way during runtime to get the size of a value in go. For my needs the marshaling overhead wasn't a big issue so I went this route.

func getRealSizeOf(v interface{}) (int, error) {
	b := new(bytes.Buffer)
	if err := gob.NewEncoder(b).Encode(v); err != nil {
		return 0, err
	}
	return b.Len(), nil
}

答案3

得分: 0

这是正确的方法,使用unsafe.Sizeof(s)。只是对于给定的类型(整数、字符串等),结果将保持不变,而不考虑确切的值。

Sizeof接受任何类型的表达式x,并返回一个假设变量v的大小(以字节为单位),就好像通过var v = x声明了v一样。该大小不包括x可能引用的任何内存。例如,如果x是一个切片,Sizeof返回切片描述符的大小,而不是切片引用的内存的大小。

参考这里

更新:

你可以使用编组(marshalling),然后使用Size()比较值的字节表示。这只是将数据转换为字节字符串的问题。

英文:

This is the correct way, using unsafe.Sizeof(s). It's just that result will remain the same for a given type - integer, string, etc., disregarding the exact value.

> Sizeof takes an expression x of any type and returns the size in bytes of a hypothetical variable v as if v was declared via var v = x. The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.

Reference here.

Update:

You can use marshalling and then compare values representations in bytes with Size(). It's only matter of converting a data to a byte string.

huangapple
  • 本文由 发表于 2017年5月30日 16:44:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/44257522.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定