dictionary - ¿Cómo obtener el tamaño de memoria de la variable en Go?
memory slice (2)
Tengo curiosidad sobre el costo de memoria del
map
y el
slice
, así que escribí un programa para comparar los tamaños.
Obtengo el tamaño de la memoria por
unsafe.Sizeof(s)
Tamaño de la
unsafe.Sizeof(s)
, pero obviamente está mal, porque cuando cambio el tamaño, la salida es la misma.
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))
}
Esta es la forma correcta, utilizando
unsafe.Sizeof(s)
.
Es solo que el resultado seguirá siendo el mismo para un tipo dado: entero, cadena, etc., sin tener en cuenta el valor exacto.
Sizeof toma una expresión x de cualquier tipo y devuelve el tamaño en bytes de una variable hipotética v como si v se declarara mediante var v = x. El tamaño no incluye ninguna memoria posiblemente referenciada por x. Por ejemplo, si x es un segmento, Sizeof devuelve el tamaño del descriptor de segmento, no el tamaño de la memoria a la que hace referencia el segmento.
Referencia unsafe.SizeOf()
Actualizar:
Puede usar la clasificación y luego comparar las representaciones de valores en bytes con
Size()
.
Solo es cuestión de convertir datos en una cadena de bytes.
unsafe.SizeOf()
y
reflect.Type.Size()
solo devuelven el tamaño del valor pasado sin atravesar recursivamente la estructura de datos y agregar tamaños de valores puntiagudos.
El segmento es una estructura relativamente simple:
reflect.SliceHeader
, y como sabemos que hace referencia a una matriz de respaldo, podemos calcular fácilmente su tamaño "manualmente", por ejemplo:
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{}))
Salida (pruébalo en Go Playground ):
Size of []int32: 12
Size of [1000]int32: 4000
Real size of s: 4012
Los mapas son estructuras de datos mucho más complejas, no entraré en detalles, pero revise esta pregunta + respuesta: Golang: calcular la huella de memoria (o longitud de bytes) de un mapa
Cálculo del tamaño de cualquier variable o estructura (recursivamente)
Si desea números "reales", puede aprovechar la herramienta de prueba de Go, que también puede realizar evaluaciones comparativas de memoria.
Pase el argumento
-benchmem
y, dentro de la función de referencia, asigne solo la memoria que desea medir:
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) }
}
(Elimine las llamadas de tiempo e impresión de
getSlice()
y
getMap()
por supuesto).
Corriendo con
go test -bench . -benchmem
Salida es:
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
valores
B/op
indican cuántos bytes se asignaron por operación.
allocs/op
indica cuántas asignaciones de memoria (distintas) ocurrieron por op.
En mi arquitectura de 64 bits (donde el tamaño de
int
es de 8 bytes), dice que el tamaño de un segmento que tiene 2000 elementos es aproximadamente 16 KB (en línea con 2000 * 8 bytes).
Se requiere aproximadamente un mapa con 1000 pares
int-int
para asignar 42 KB.