Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Eventos y garbage

Iniciado por blau, 26 de Diciembre de 2011, 05:23:37 PM

« anterior - próximo »

blau

Buenas,

últimamente me he concienciado un poquito más con el garbage collector... me he hecho un componente gráfico para ver como se va comportando el recolector... y todavía me asombro de que algunas cosas que he hecho en el pasado fuesen decentemente...       ^_^'  

el caso es que he estado haciendo un sistema de particulas un poquito especial... y me he dado cuenta de que producía mucho garbage pese a que tenía implementado un pool de objetos y no sabía por que...

investigando me he dado cuenta de que el problema viene dado por las lineas comentadas de este código, que  gestiona cuando una particula entra en una zona, y se suscribe a la muerte de la particula para procesar su salida.

pues bien esto tan nimio hace que en un PC de oficina no se puedan gestionar ni 512 particulas, el recolector se enchufa cada menos de 2 segundos y framea que es un gusto, eliminando este codigo y gestionando la salida del contenedor desde la particula hace que el recolector se llame cada 9 segundos y que vaya a 60 fps sin problemas...

y la pregunta es ¿por que? ¿por que? ¿por que?

Código (csharp) [Seleccionar]
Container.cs

        public void ProcessParticleEnter( Particle particle )
        {
           Debug.Assert( !ContainedParticles.Contains( particle ) );
           ContainedParticles.Add( particle );
         // particle.Died += new Action<AstroObject>( particle_Died );
       }

       //void particle_Died( AstroObject obj )
       //{
       //    ProcessParticleExit( obj as Particle );
       //}

       public void ProcessParticleExit( Particle particle )
       {
           Debug.Assert( ContainedParticles.Contains( particle ) );
           ContainedParticles.Remove( particle );
       //    particle.Died -= particle_Died;
       }


Particle.cs

       protected override void LocalDie()
       {
           if (CurrentContainer!=null) CurrentContainer.ProcessParticleExit(this);
       }


Vicente

Con cada particula estas creando un delegado, asi que con muchas el recolector sufre, lo que me sorprende es que el recolector de PC sufra con 512...

Una herramienta buena para este tipo de cosas de memoria es el CLR Profiles, o tambien usar los performance counters para ver que leches hace el GC.

XÑA

Yo lo que hacía es que nunca hacía adds y removes, simplemente cambiaba el estado. En tu caso, siempre tienes un nº constante de partículas, así que...¿para que remover el elemento de la lista y luego insertar otro nuevo?

Recuerda que un List<> es un array, que al crecer o decrecer, obliga a copiar TODA la memoria del array al nuevo array.


blau

#3
@Vicente: Sufre porque el PC tiene poca memoria y aparte las particulas consumen CPU a porrillo con lo que el sistema está en el limite.. y el uso de cpu del recolector es la guinda que mandaba todo al carajo...

Y sigo sin entender porque tiene que crear garbage al asignar un evento, una operacion interna deberia hacerse para que no produjese garbage.

@Xña: Lo que estoy haciendo es un sistema de partículas para simular un fluido, y para que no interactuen todas con todas, lo he dividido en un grid de forma que cada partícula  registra su entrada o su salida en la celda correspondiente.

Y respecto a las listas, ¿estas seguro de que hace eso? para mi que copiaba las referencias unas sobre otras y punto, no creo que reserve memoria para todo de nuevo. De todas formas tenia pensado cambiarlo a listas enlazadas... pero me ha dado mucha pereza. ^_^'

Vicente

Cuantas particulas puedes poner hasta que se te peta igual que cuando usas el evento? (asi mas o menos). Por curiosidad. Esto deberia ser todo Gen0 y eso es rapidísimo en general.

Sobre la listas, cuando una lista tiene que crecer, se reserva el doble de memoria que la lista anterior, pero esta lista solo tiene referencias... De todas formas si sabes cuantas particulas tienes como maximo, puedes crear las listas con ese tamaño desde el principio y listo :)

blau

Ahora entraban unas 100 particulas más, pero claro el orden del algoritmo es exponencial....

acabo de probarlo en casa y esto ahora es otra cosa... ya llega a 1024 perticulas a 60 fps, aunque cuando se aplica una fuerza externa baja unos 10 fps y enseguida se recupera...

pero lo mejor es que ahora tarda cerca de 50 segundos en activarse el recolector....:)


Vicente

Mola el video si señor :) El GC en el fondo tarda segun la RAM sobre todo, en casa tendras bastante más :)

XÑA

¡¡Qué chulo!!  :o

Yo tengo un libro que se llama algo así como: C# inside CLR. Creo recordar que en el tema de las listas el tema es así:

Si no hay memoria reservo el doble.
Copy una lista sobre la nueva
Marco la vieja para liberar

Pero no recuerdo qué hace al hacer un Remove....  :-\

Leyendo este libro te das cuenta de que programar bien y programar optimizado se dan de leches algunas veces. Te dice, por ejemplo, que Outlook crea no se cuantas Threads, y que eso está mal, que debería gestionarlo de otra forma, porqué eso está destrozando el sistema. Por lo visto .Net tiene un ThreadPool o algo así,  y si lo tienes en cuenta el sistema va mucho mejor.
Luego habla sobre las famosas Propiedades, que en realidad son llamadas a métodos, y te explica la diferencia entre métodos virtualizados y directos. ¿Conocías la diferencia? Yo no  :(
El problema es...¿cómo lo sabes? Yo no lo sabía hasta que leí el libro, así que...  :(

blau

Cita de: Vicente en 27 de Diciembre de 2011, 01:41:42 AM
Mola el video si señor :) El GC en el fondo tarda segun la RAM sobre todo, en casa tendras bastante más :)

Algo asi como 7 Gb mas,  8), aunque antes de optimizar se me venia a 10 segundos entre llamada y llamada.

Para el tema de los frames por segundo también he optimizado, no permitiendo que una celda contenga mas de 50 partículas y matando a las partículas sobrantes. De esta forma los fps  van mucho mas estables. :)

@XÑA:

Yo creo que al hacer un remove o un insert simplemente desplaza las referencias, si no produciría un garbage de la leche...

a priori es cierto que se desconocen cosas, pero la intuición hace un papel muy importante, aunque a mi me haya fallado con los eventos,... en fin... que después de pasarlas canutas te lees un libro de esos y flipas en colores... :)


El tema es que se me ha caído un mito... todavía no entiendo por qué un mecanismo interno del lenguaje que se limita a manejar una lista de delegados genera tanta basura.... en todo caso... lo que debo hacer es intentar no suscribir/desuscribir a eventos tan frecuentemente....

Me siento como si hubiera desbloqueado un logro en la programación de videojuegos con C#. :P

Vicente

El libro que comentas es el CLR via C#, que esta bastante bien "en general". Tiene cosas muy buenas, pero tambien al amigo se le va la pinza a veces, por ejemplo declarando todas las variables de forma: Int16, Int32,... En vez de usar int y cosas asi.

Sobre lo de programar bien y programar optimo, para gustos colores normalmente. Yo pienso que hay que programar bien, usar un profiler para medir rendimiento, y con esos datos optimizar donde haga falta. Pero no programar "optimizado" de buenas a primeras.

XÑA

Yo estoy deacuerdo con Vicente!!  :P

blau

Si, está claro... pero dentro de programar bien va la optimizacion, me refiero pero si vas a desarrollar un juego y sabes que te va a hacer falta un pool de objetos... programar bien es tenerlo en cuenta de primeras.  :)

Un saludo.




WaaghMan

Cita de: Vicente en 27 de Diciembre de 2011, 11:18:08 AM
El libro que comentas es el CLR via C#, que esta bastante bien "en general". Tiene cosas muy buenas, pero tambien al amigo se le va la pinza a veces, por ejemplo declarando todas las variables de forma: Int16, Int32,... En vez de usar int y cosas asi.

Sobre lo de programar bien y programar optimo, para gustos colores normalmente. Yo pienso que hay que programar bien, usar un profiler para medir rendimiento, y con esos datos optimizar donde haga falta. Pero no programar "optimizado" de buenas a primeras.


Ahí.

Personalmente, la generación de basura que más me repatea (creo que ya lo comenté en otro sitio) es la de iterar sobre ciertos casos de IEnumerable:


Dictionary<int,string> d = new Dictionary<int,string>();
foreach(string s in d.Values)
{
  ...
}


Eso de ahí genera basura, también pasa al hacerlo sobre un AsReadOnly(), etc. Son la mayor parte de casos con los que me he topado en APIs de terceros (Farseer y Jitter), y son bastante engorrosos de corregir. Se deben a que en cierto momento el Enumerator que tienen las colecciones (una estructura), se le hace boxing para pasarlo como IEnumerable.
Milkstone Studios - Autores de Avatar Ninja!, Little Racers, MotorHEAT y Wool en Xbox Live Indie Games

Vicente

Cita de: blau en 27 de Diciembre de 2011, 04:37:08 PM
Si, está claro... pero dentro de programar bien va la optimizacion, me refiero pero si vas a desarrollar un juego y sabes que te va a hacer falta un pool de objetos... programar bien es tenerlo en cuenta de primeras.  :)

Un saludo.

Totalmente de acuerdo :)






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.