PDA

Ver la versión completa : Sources: Codigo para profilling.



una-i
28/06/2006, 07:02
Bueno cono voy corriendo no voy a dar muchas explicaciones, me he hecho esto para auydarme a profilear el psx4all...

Consiste en código y unas macros para que poner y calcular tiempos sea mas fácil...

El truco esta en que usa constructores y destructores de c++ para que no haya que emarcar el final de la "cuenta".

Acontinuación el codigo a meter en "profiller.h"



#ifndef PROFILLER_H
#define PROFILLER_H

///////////////////////////////////////////////////////////////////////////////
// Profilling helper functions, minimal libs are needed for timer.
#include "minimal.h"

///////////////////////////////////////////////////////////////////////////////
// Minimal extensions used for better accuracy and speed.
/*
unsigned long gp2x_timer_raw(void)
{
return gp2x_memregl[0x0A00>>2];
}

unsigned long gp2x_timer_raw_to_ticks(unsigned long ticks)
{
return ticks/gp2x_ticks_per_second;
}

unsigned long gp2x_timer_raw_second()
{
return 7372800;
}
*/

///////////////////////////////////////////////////////////////////////////////
// Profiler Interface/Macro Definitions
///////////////////////////////////////////////////////////////////////////////
#ifndef ENABLE_PROFILLER

#define PROFILE_NAMED_FENCE(name,time,count)
#define PROFILE_NAMED_PAUSE(name)
#define PROFILE_NAMED_RESUME(name)

#define PROFILE_FENCE(time,count)
#define PROFILE_PAUSE()
#define PROFILE_RESUME()

#define PROFILE_RATIO(time,total) (.0f)
#define PROFILE_RESET(time,count)

#else
///////////////////////////////////////////////////////////////////////////////
// Profiller claas implementation
class Profiller
{
u32& uTotalTime;
u32 uLocalTime;

public:
Profiller(u32& _uTime, u32& _uCount) : uTotalTime(_uTime)
{
++_uCount;
uLocalTime = gp2x_timer_raw();
}

void Pause()
{
uTotalTime += gp2x_timer_raw()-uLocalTime;
}

void Resume()
{
uLocalTime = gp2x_timer_raw();
}

~Profiller()
{
uTotalTime += gp2x_timer_raw()-uLocalTime;
}

static void Reset(u32& _uTime, u32& _uCount)
{
_uTime = _uCount = 0;
}

static float Ratio(u32 _uTime, u32 _uTotal)
{
return float(_uTime)/float(_uTotal);
}
};

///////////////////////////////////////////////////////////////////////////////
// Profiller macros
#define PROFILE_NAMED_FENCE(name,time,count) Profiller __myProfiller##name(time,count)
#define PROFILE_NAMED_PAUSE(name) __myProfiller##name.Pause()
#define PROFILE_NAMED_RESUME(name) __myProfiller##name.Resume()

#define PROFILE_FENCE(time,count) PROFILE_NAMED_FENCE(0,time,count)
#define PROFILE_PAUSE() PROFILE_NAMED_PAUSE(0)
#define PROFILE_RESUME() PROFILE_NAMED_RESUME(0)

#define PROFILE_RATIO(time,total) Profiller::Ratio(time,total)
#define PROFILE_RESET(time,count) Profiller::Reset(time,count)


#endif //ENABLE_PROFILLER


///////////////////////////////////////////////////////////////////////////////
#endif // PROFILLER_H



Para usarlo lo único que hay que hacer es lo siguiente



// estas variables almacenaran mis medidas
unsigned int variableDeTiempo,variableDeCuenta

void miFuncion()
{
// La siguiente marca creas una medida desde ella hasta que se sale de la función
PROFILE_FENCE(variableDeTiempo,variableDeCuenta);
for(int i=0;i<10000;++i)
{
HazAlgoDificil();
}
}

void miRun()
{
// A Continuación leemos los "contadores" y reseteamos las variables
printf("tiempo %d, veces%d", variableDeTiempo,variableDeCuenta);
PROFILE_RESET(variableDeTiempo,variableDeCuenta);
}


El mayor "problema" que tiene el sistema es que las variables de "profile" tiene que estar "declaradas en algun sitio, eso se puede "arreglar" con mas ingenieria, pero esto es lo que necesitaba para lo que estaba haciendo..

Si queremos medir partes de una funcion hay que hacer lo siguiente, ya que el tiempo esta ligado al "scope" en el que se mete la marca.


void miFuncion()
{
// medicion completa de toda la función
PROFILE_FENCE(variableDeTiempoTotal,variableDeCuen taTotal);

if(bOpcion)
{
PROFILE_FENCE(variableDeTiempoParcialA,variableDeC uentaParcialA);
for(int i=0;i<10000;++i)
HazAlgoDificil();
}
else
{
PROFILE_FENCE(variableDeTiempoParcialB,variableDeC uentaParcialB);
for(int i=0;i<100;++i)
HazAlgoDificilTambien();
}

// el código siguiente no se medirá con lo cual
// variableDeTiempoTotal > ( variableDeTiempoParcialA + variableDeTiempoParcialB + variableDeTiempoParcialC )
// y tiempo de este for será igual a variableDeTiempoTotal - ( variableDeTiempoParcialA + variableDeTiempoParcialB + variableDeTiempoParcialC )
for(int i=0;i<100;++i)
HazAlgo();

// para medir una parte aislada de codigo lo metemos entre {}
{
PROFILE_FENCE(variableDeTiempoPArcialC,variableDeC uentaParcialC);
for(int i=0;i<100;++i)
HazAlgoMas();
}

}


La dependenci de las minimal es algo facilmente solucionable, si quereis usarlo con sdl solo tenies que sustituir la lamada a gp2x_timer_read() por una a SDL_GetTicks() creo recordar, teniendo en cuenta que estas medidas son en "unidades de tick" no en segundos...

En breve supongo publicare una version "mejorada" con soporte "de serie" para la SDL y con ayudas para calcular porcentajes sobre un total... que es muy fácil y biene bien para saber que areas de un programa se estás llebando más tiempo.

Por último comentar que hay que hacer un #define ENABLE_PROFILLER en vuestro código o el sistema esta preparado para eliminar completamente todo rastro del profiler si no esta definido ese "símbolo"


Unai.

Locke
28/06/2006, 08:08
Toma ya! Gracias a todos los que ultimamente os da por compartir funciones de utilidad, que facilitan muchas tareas.

Estas en concreto no son lo mio, ya que el C++ no me va, pero para quienquiera que necesite optimizaciones de codigo van a venir muy bien. :)

una-i
29/06/2006, 01:54
Estas en concreto no son lo mio, ya que el C++ no me va, pero para quienquiera que necesite optimizaciones de codigo van a venir muy bien. :)

Bueno no tienes que hacer codigo c++ para usarlas, simplemente con que renombres tus ficheros a .cpp, o decirle en el makefile que use el g++ ya te funcionaria todo igual... el codigo con "formato C" compila casi sin ningun cambio con el g++ y además el g++ suele optimizar mejor....


Unai.

< - >
Por cierto, nohe pillado el post de mejoras a la minimal lib, pero para hacer cosas como estas si os fijais en las minimal, cadavez que se hace un gp2x_timer_read() se esta haciendo una dicision para convertir los "ticks" del reloj de la gp2x a las unidades por segundo que le hesmo dado al inicializar las minmal.

Esta funcionalidad es muy útil para llevar cuantas sencillas etc, pero cuando pretendemos hacer miles de mediciones por frame estamos haciendo miles de divisiones por frame y perdiendo "resolución" en cada una de ellas. Además el profiler solo entiende de numeros, no los interprete, por tanto podriamo almacenar los ticks "nativos" de la makina sin convertirlos ni nada, y caundo vayamos a "presentar" los resultados convertirlos una sola vez en lugar de convertir cada medida.

Por eso si seguis con la libreria "adicional" a la minimal, una función de gp2x_raw_timer_read() y gp2x_raw_Timmer_convert() serian lo ideal porque a parte de hacer el profiler mucha más rapido seria mucho más preciso.

Bueno y ahora el ejemplo de uso que me faltaba.
Imaginemos la estructura de un emulador, esto es un ejemplo de como sacar la estadistica de cuanto tiempo estamos perdiendo en cada parte del emulador.




unsigned int DrawTime,DrawCount;
unsigned int InputTime,InputCount;
unsigned int CpuTime,CpuCount;
unsigned int FrameTime,FrameCount;

void Draw()
{
PROFILE_FENCE(DrawTime,DrawCount);
// código
}

void Input()
{
PROFILE_FENCE(InputTime,InputCount);
// código
}

void Cpu()
{
PROFILE_FENCE(CpuTime,CpuCount);
// código
}

void Run()
{
// Bucle princupal del emulador
while(1)
{
{
PROFILE_FENCE(FrameTime,FrameCount);
Input(); // procesamos input de usario
Cpu(); // emulamos la cpu
Draw(); // pintamos
}
// calculamos el porcentage te tiempo de frame de cada "area"
gp2x_printf(NULL, 0,10,"InputTime(%2d): %04.4g%%", InputCount, PROFILE_RATIO((InputTime*100),FrameTime) );
gp2x_printf(NULL, 0,10,"CpuTime(%2d): %04.4g%%", CpuCount, PROFILE_RATIO((CpuTime*100),FrameTime) );
gp2x_printf(NULL, 0,10,"DrawTime(%2d): %04.4g%%", DrawCount, PROFILE_RATIO((DrawTime*100), FrameTime) );

// reseteamos las medidas
PROFILE_RESET(InputTime,InputCount);
PROFILE_RESET(CpuTime,CpuCount);
PROFILE_RESET(DrawTime,DrawCount);
PROFILE_RESET(FrameTime,FrameCount);
}
}



Con esto deberiamos depoder hacernos una idea de por donde se nos escapa el tiempo.

La siguiente "entrega" contaremos como hacer lo mismo con una estructura ligeramente mas "complicada".


Unai

D_Skywalk
29/06/2006, 05:10
Joe, gracias Una-i esto me vendra fenomenal cuando en el 2023 termine el RPG y me ponga de nuevo con el Pituka ;)

Un Saludo compa!

una-i
29/06/2006, 06:45
Joe, gracias Una-i esto me vendra fenomenal cuando en el 2023 termine el RPG y me ponga de nuevo con el Pituka ;)

Un Saludo compa!

Hey hey no tanto, que yo cuento con el pituka en l agp2x antes de navidades, que hay muchos juegos a "rescatar" de amstrad!!!!

Bueno da igual, tu andabas metido en las "mejoras" a las minimal??


Unai.

una-i
24/07/2006, 07:21
También aprobecho para añadir un ejemplo para obetenr medidas mas "estables" de framerates..

Lo que hace esta rutina es que en función de cada cuantas veces quedaramos actualizar por segundo el contador de frames (cuanto mayor sea el intervalo mas precisión), y teniendo en cuenta el tiempo total que han costado el frame calcula el framrate actual.




float frameRate;
newtime = gp2x_timer_raw();
if( (diffintime=newtime-systime) >= (gp2x_timer_raw_second()/5) ) // poll 5 times per second
{
systime = newtime;
frameRate = frameRateCounter*gp2x_timer_raw_second() / diffintime;
frameRateCounter = 0;
}

Rivroner
24/07/2006, 07:59
Joe, gracias Una-i esto me vendra fenomenal cuando en el 2023 termine el RPG y me ponga de nuevo con el Pituka ;)

Un Saludo compa!
Espero que al menos Kaosoverride saque pronto nueva versión de su Caprice [Ahhh] poruqe yo toy desesperao por jugar a mis jueguecillos de toda la vida de Amstrad al 100% en la GP2X.
Por cierto el otro día intenté usar un script para el Caprice para ponerlo a 275 y ver lo que mejoraba pero me da un pantallazo en negro de cojones :D Para el emu de SNES hago lo mismo y si me tira.He cambiado bien la ruta y eso, no sé que será.Os lo pongo por si veis algún fallo y me podéis ayudar.Gracias.Perdona por entrometerme en tu hilo Una-i :p Si alguno de los 2 me puede ayudar os lo agradecería mucho :) Es que le falta sólo un 20% de mejora para ir bien con sonido y a lo mejor este overclock lo consigue.Me molesta más que se atasque el sonido que el que no vaya la imagen fluida 100% con sonido.Ya sé que hay juegos en los que no es necesario el sonido, pero me gusta jugar con sonido incluso a los jueguecillos en los que sólo suenan FX [wei4]