2010-05-28 6 views
5

Estoy codificando un clon de ruptura. Tenía una versión en la que solo tenía un nivel profundo de estructuras. Esta versión funciona a 70 fps.C: ¿usar muchas estructuras puede hacer que un programa sea lento?

Para mayor claridad en el código, decidí que el código debería tener más abstracciones y crear más estructuras. La mayoría de las veces tengo dos dos estructuras de tres niveles de profundidad. Esta versión funciona a 30 fps.

Dado que existen otras diferencias además de las estructuras, le pregunto: ¿usar muchas estructuras en C puede ralentizar el código significativamente?

Por ejemplo, en la segunda versión, estoy usando:

struct Breakout 
{ 
    Ball ball; 
    Paddle paddle; 
    Level* levels; 
} 

struct Level 
{ 
    Bricks* bricks; 
} 

Por lo tanto, estoy usando un montón de veces breakout.levels [level_in_play] .bricks [i] .Visible por ejemplo. ¿Será esta una posible causa?

Gracias.

+0

¿Las estructuras son "profundas" debido a su valor o referencia? ¿Estás haciendo asignación de memoria dinámica? – WhirlWind

+0

Estoy usando mallocs sí. – nunos

+1

Si asigna dinámicamente memoria en una ruta sensible al rendimiento, eso definitivamente podría desacelerar las cosas; Me gustaría ejecutar un perfil rápido en sus dos versiones de código para ver por qué, sin embargo. Tendría que mostrar un poco del código, no solo algunas estructuras de datos. – WhirlWind

Respuesta

11

Hacer muchas desreferencias de puntero puede ser un golpe de rendimiento. Al dividir una gran estructura arriba en estructuras más pequeñas, suceden dos cosas:

  1. Acceso a un miembro de una sub-estructura requiere una desreferenciar adicional puntero y la memoria buscar, que es ligeramente más lento, y
  2. Usted puede reducir locality of reference, que causa más errores de caché y fallas de página y puede reducir drásticamente el rendimiento.

La localidad de referencia uno es probablemente lo que te está mordiendo aquí. Si es posible, intente asignar estructuras relacionadas en el mismo bloque malloc, lo que aumenta la probabilidad de que se almacenarán en caché.

+0

Agregue algunos perfiles, y esto es perfecto. –

+0

¿Alguna vez descubrió que la desreferencia de un puntero es la causa de un problema de rendimiento? Claro que causará una cantidad pequeña, casi infinitesimal de trabajo extra, pero no creo que la eliminación de referencias del puntero se convierta en un problema a menos que lo esté ejecutando miles de veces por cada otra operación. La creación de perfiles sería la única forma de evitar la pesimismo prematura. – Allbite

+0

@Allbite: Aparentemente no era lo suficientemente claro. No es la desreferencia la que puede causar el impacto en el rendimiento (al menos, no es probable, estoy seguro de que hay casos límites en los que importa), es el hecho de que potencialmente puedes poner datos estrechamente relacionados en diferentes lugares de la memoria . Por supuesto, la creación de perfiles de la aplicación es importante, pero la ubicación de los problemas de referencia puede ser difícil de detectar con la ejecución de un generador de perfiles, ya que el generador de perfiles ya está afectando el rendimiento y la ubicación de la referencia simplemente adjuntándolos. –

4

En primer lugar, es fácil y tentador adivinar cuál es el problema. Lo difícil de las conjeturas es que a veces tienen razón. Pero, ¿por qué adivinar, cuándo puedes averiguar para qué estás tomando el tiempo? I recommend this approach.

Dicho esto, esta es mi suposición. malloc y free, si pasa un solo paso por ellos en el nivel de lenguaje ensamblador, probablemente estén haciendo mucho más de lo que pensaba. Solo asigno memoria a las estructuras si sé que no lo haré a una frecuencia particularmente alta. Si I debe asignarlos/desasignarlos dinámicamente, a alta frecuencia, es útil contar con una lista gratuita de copias usadas, así que puedo sacarlas de la lista en lugar de ir a malloc todo el tiempo.

Sin embargo, tome algunas instantáneas. Lo más probable es que pueda solucionar una serie de problemas y hacerlo un lote mucho más rápido.

4

Agregar capas adicionales de desreferenciación puede causar una pequeña (muy poca) cantidad de sobrecarga de desaceleración. La razón es que cada -> que ve el compilador significa que tiene que hacer una búsqueda y compensación de memoria adicional. Por ejemplo, c->b->a requiere que el compilador cargue el puntero c en la memoria, lo refiera, desplace a b, desreferencia que, desplace a a, desreferencia eso, luego cargue a en la memoria. Eso es bastante trabajo de memoria. Hacer c.b.a requiere la carga inicial de c, un solo agregado, y luego la carga directa de a desde la memoria. Eso es 2 cargas contra 5.

A menos que este tipo de trabajo se haga una tonelada en bucles pequeños y apretados, no se acumulará durante tiempo.Sin embargo, si estás haciendo esto en pesados ​​bucles internos (y tu compilador no te está ayudando), podría sumarse. Para esos casos, considere almacenar en caché el puntero struct de nivel más bajo y trabajar desde allí.

Dicho esto, cada vez que aparezca el rendimiento, el primer paso es el perfil. Sin un perfil, estás adivinando. Ha hecho una afirmación de que struct derelessly es la raíz de su rendimiento, pero sin un perfil válido y actualizado (en una compilación de lanzamiento) está adivinando y probablemente perdiendo tiempo.

+0

+1 - En mi experiencia, es poco probable, aunque no imposible, que las estructuras causen una gran desaceleración. Perfil. –

1

No hay "muchas estructuras" en sí mismas, otras que ofrecen la posibilidad de acceder a una mayor cantidad de memoria. "mucha indirección", sin embargo, es más probable que sea una causa. Debe tener en cuenta cuántos accesos de memoria se generan para llegar a los datos reales. La proximidad y el tamaño de los datos también pueden afectar el almacenamiento en caché, pero es mucho más difícil de analizar.

Además, como mencionó en un comentario que está realizando una asignación de memoria dinámica, el tiempo necesario para encontrar una asignación de un bloque no es determinista y es variable. Si está asignando y liberando bloqueos repetidamente durante la ejecución del algoritmo (en lugar de preasignar en la inicialización, por ejemplo), esto puede causar degradación y variabilidad en el rendimiento.

Si tiene una herramienta de creación de perfiles, haga un perfil del código para ver dónde se produce el golpe de rendimiento.

Cuestiones relacionadas