performance - programar - Rendimiento: cortes de estructuras vs. cortes de punteros a estructuras
punteros en c youtube (2)
¿Puede proporcionar pautas generales sobre cuándo trabajar con estructuras directamente en lugar de cuándo trabajar con punteros a estructuras?
No, depende demasiado de todos los otros factores que ya has mencionado.
La única respuesta real es: punto de referencia y ver. Cada caso es diferente y toda la teoría en el mundo no hace la diferencia cuando tienes tiempos reales para trabajar.
(Dicho esto, mi intuición sería usar punteros, y posiblemente una sync.Pool
para ayudar al recolector de basura: http://golang.org/pkg/sync/#Pool )
A menudo trabajo con rebanadas de estructuras. Aquí hay un ejemplo para tal estructura:
type MyStruct struct {
val1, val2, val3 int
text1, text2, text3 string
list []SomeType
}
Así que defino mis sectores de la siguiente manera:
[]MyStruct
Digamos que tengo alrededor de un millón de elementos y estoy trabajando mucho con la porción:
- Añado nuevos elementos a menudo. (La cantidad total de elementos es desconocida)
- Lo ordeno de vez en cuando.
- También elimino elementos (aunque no tanto como agregar elementos nuevos).
- Leo elementos a menudo y los paso por alto (como argumentos de funciones).
- El contenido de los elementos en sí no cambia.
Tengo entendido que esto lleva a un gran revuelo de la estructura real. La alternativa es crear una porción de punteros a la estructura:
[]*MyStruct
Ahora las estructuras permanecen donde están y solo tratamos con punteros que, supongo, tienen una huella más pequeña y, por lo tanto, harán que mis operaciones sean más rápidas. Pero ahora le estoy dando al recolector de basura mucho más trabajo.
- ¿Puede proporcionar pautas generales sobre cuándo trabajar con estructuras directamente en lugar de cuándo trabajar con punteros a estructuras?
- ¿Debo preocuparme por la cantidad de trabajo que le dejo al GC?
- ¿La sobrecarga de rendimiento de copiar una estructura frente a copiar un puntero es despreciable?
- Tal vez un millón de elementos no sea mucho. ¿Cómo cambia todo esto cuando la porción es mucho más grande (pero aún cabe en la memoria RAM, por supuesto)?
Solo tengo curiosidad sobre esto yo mismo. Ran algunos puntos de referencia:
type MyStruct struct {
F1, F2, F3, F4, F5, F6, F7 string
I1, I2, I3, I4, I5, I6, I7 int64
}
func BenchmarkAppendingStructs(b *testing.B) {
var s []MyStruct
for i := 0; i < b.N; i++ {
s = append(s, MyStruct{})
}
}
func BenchmarkAppendingPointers(b *testing.B) {
var s []*MyStruct
for i := 0; i < b.N; i++ {
s = append(s, &MyStruct{})
}
}
Resultados:
BenchmarkAppendingStructs 1000000 3528 ns/op
BenchmarkAppendingPointers 5000000 246 ns/op
Llévate: estamos en nanosegundos. Probablemente insignificante para rodajas pequeñas. Pero para millones de operaciones, es la diferencia entre milisegundos y microsegundos.
Por cierto, traté de ejecutar el benchmark de nuevo con porciones que fueron preasignadas (con una capacidad de 1000000) para eliminar la sobrecarga de append () copiando periódicamente el conjunto subyacente. Las estructuras adjuntas cayeron en 1000ns, los punteros añadidos no cambiaron en absoluto.