En ocasiones es necesario medir el tiempo que transcurre en completarse alguna operación en nuestro código, ya sea para optimizar su rendimiento o por cualquier otra razón. Para ello, en ciertas situaciones se puede utilizar la propiedad 'Now' de la clase DateTime, que devuelve una instancia de DateTime con la fecha y hora actuales. Se toma este valor en dos puntos diferentes del código y se restan para obtener el tiempo transcurrido.
Esta aproximación sólo será válida si la duración de la operación a realizar es relativamente larga, pero, en muchas ocasiones, la precisión ofrecida por este método no será suficiente y obtendremos que el tiempo transcurrido es 0, lo que no da mucha información. Para estos casos se usan habitualmente las funciones QueryPerformanceFrequency y QueryPerformaceCounter del API de Windows. Si se realiza una búsqueda sobre estas funciones en cualquier buscador, se verán cientos de artículos explicando su funcionamiento, con lo que no voy a extenderme aquí en su uso.
Lo que si puede resultar interesante en este caso es saber que estas funciones también se pueden utilizar en Windows CE, y, por tanto, están disponibles en Pocket PC y SmartPhone. Para utilizarlas hay que declararlas mediante P/Invoke de la siguiente forma:
[DllImport("coredll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
[DllImport("coredll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
Como se ve están definidas en la librería coredll.dll que contiene casi todo el API nativo de Windows CE.
Para mostrar su uso, se adjunta un ejemplo (PerformanceMeter.zip) que contiene una solución de Visual Studio 2005 para Windows Mobile 2005 Pocket PC, con lo que es necesario disponer del SDK de Pocket PC 2005 para poder probarlo tal cual está (si no se dispone de ese SDK, se puede crear una solución en Compact Framework 2.0 para Pocket PC 2003 y añadir las clases a ese proyecto para utilizarlas). Esta solución contiene 2 proyectos. El primero, llamado PerformanceMeter, contiene tres clases cuyo objetivo es facilitar la realización de pruebas de rendimiento a lo largo del código de manera cómoda y reutilizable. El proceso sería el siguiente:
- Crear una instancia de la clase PerformanceMeter
- Crear un nuevo test de rendimiento mediante la llamada a PerformanceMeter.NewTest(), que devuelve una instancia de la clase PerformanceTest. Esta instancia es almacenada automáticamente en una colección interna de PerformanceMeter.
- En el punto en el que se quiera iniciar el test, se hace una llamada a la función Start() de la instancia de la clase PerformanceTest obtenida en el paso anterior.
- Cuando se quiera finalizar el test, se hace una llamada a la función Stop() de la misma instancia anterior.
- Para ver los resultados de los test realizados hay que consultar la propiedad PerformanceTests que contiene la colección de pruebas realizadas.
El objetivo de estas clases es el de poder realizar varios tests de rendimiento a lo largo de las pruebas de una aplicación dada para posteriormente poder crear un informe de cómo han resultado las pruebas, de la manera menos intrusiva para el código posible. Cada test contiene la siguiente información:
- Nombre y descripción del test, útil si se va a disponer de varios tests y se quiere sacar un listado de ellos.
- Número de iteraciones que realiza el test de un código determinado. Este valor sólo es una propiedad que se usa para calcular una media, de manera que si se entre la ejecución de la función Start y la función Stop el código realiza 10 iteraciones, la podemos establecer con ese valor para que devuelva la media por iteración.
- Fecha y hora de inicio y fin del test, obtenidos con DateTime.Now. Hay que tener en cuenta que los contadores de precisión no devuelven la hora.
- Tiempo de ejecución del test en milisegundos obtenidos con la resta de los anteriores.
- Duración media por iteración en alta precisión (propiedad AverageDurationPerIteration - tipo double), si se miden varias iteraciones.
- Tiempo de ejecución del test en alta precisión (propiedad TotalDuration - tipo Double)
Como ejemplo del uso de estas clases está el segundo proyecto incluido en la solución, PerformanceTests. Este proyecto presenta un formulario para Pocket PC y un menú, Tests, que hace, a modo de ejemplo, dos tests, uno de concatenación de strings y otro de búsquedas en un documento XML.
El primer test compara lo que tarda el código en concatenar 2000 cadenas con y sin usar la clase StringBuilder, y el otro test mide lo que tarda el código en acceder un nodo de un documento XML con XmlDocument y lo compara con lo que se tarda en acceder al mismo nodo usando XmlTextReader.
Cuando finalizan los tests, muestra en la ventana los tiempos medidos.
En este ejemplo, para que los resultados sean más reales, conviene ejecutar cada test dos veces y usar los resultados de la segunda ejecución, ya que la primera vez que se ejecutan cada uno de ellos hay un tiempo de carga e instanciación de clases que hacen que el primer test sea mucho más lento que el segundo.
Espero que resulte interesante.
PerformanceMeter.zip (63,17 KB)