Recuerda que en las ciencias un problema suele tener más de una soluciones, por lo tanto aquí enseñaremos lo más óptimo y mejor.
Solución:
unsafe.SizeOf()
y reflect.Type.Size()
solo devuelve el tamaño del valor pasado sin atravesar recursivamente la estructura de datos y agregar tamaños de valores puntiagudos.
La rebanada es una estructura relativamente simple: reflect.SliceHeader
, y como sabemos que hace referencia a un respaldo array, 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 el 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 consulte esta pregunta y respuesta: Golang: cálculo de 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 pruebas comparativas de memoria. Pasa el -benchmem
argumento, 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 cronometraje e impresión de getSlice()
y getMap()
por supuesto.)
corriendo con
go test -bench . -benchmem
La 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
los valores le dicen cuántos bytes se asignaron por operación. allocs/op
dice cuántas asignaciones de memoria (distintas) ocurrieron por operación.
En mi arquitectura de 64 bits (donde el tamaño de int
es de 8 bytes) indica que el tamaño de un segmento que tiene 2000 elementos es de aproximadamente 16 KB (en línea con 2000 * 8 bytes). Un mapa con 1000 int-int
pares requeridos aproximadamente para asignar 42 KB.
Esto genera algunos gastos generales de cálculo de referencias, pero descubrí que es la forma más sencilla durante el tiempo de ejecución de obtener el tamaño de un valor en marcha. Para mis necesidades, la sobrecarga de clasificación no fue un gran problema, así que seguí esta ruta.
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
Nos puedes auxiliar nuestra ocupación dejando un comentario y puntuándolo te damos la bienvenida.