2017-05-30 66 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