2008-11-01 15 views
5

¿Cómo puedo contar operaciones en C++? Me gustaría analizar el código de una manera mejor que solo medirlo, ya que a menudo el tiempo se redondea a 0 milisegundos.¿Cómo puedo contar operaciones en C++?

+0

especificar qué Qué quiere decir con " contar operaciones ". Hay instrumentos para analizar las llamadas a funciones en su código como un generador de perfiles, pero también puede ver, agregando el interruptor apropiado, el código ensamblador generado. –

Respuesta

3

Puede realizar mediciones precisas leyendo el contador de marca de tiempo (tsc) de la CPU, que se incrementa en uno en cada reloj de la CPU.

Lamentablemente, la lectura se hace resumiendo algunas instrucciones del ensamblador en el código. Dependiendo de la arquitectura subyacente, el costo de la lectura varía entre ~ 11 (AMD) y ~ 33 (Intel) tsc. Con una CPU de 1 Ghz prácticamente puedes tener la precisión de nano segundos.

Con el fin de realizar una medida fiable y no invasiva de una sección de código que puede:

  • evitar que la frecuencia de la CPU de escala mediante la desactivación de las funciones de la CPU como Cool'n AMD bastante o Intel SpeedStep.
  • repita la prueba varias veces, recoja las medidas en una matriz y luego guarde los datos en el archivo para un análisis fuera de línea.
  • elija una política de programación en tiempo real para el proceso bajo prueba como SHED_RR o SHED_FIFO. Las políticas en tiempo real reducen el número de cambios de contexto entre el proceso bajo prueba y otros procesos normales/hilos del kernel, que están bloqueados.
  • bloquea todo el espacio de direcciones virtuales del proceso en la memoria RAM mediante la llamada al sistema mlockall().

Here se puede encontrar una clase de C++ cuasi-portátil que escribí para Linux, derivado del núcleo de Linux y diseñado para leer TSC para las arquitecturas i386, x86_64 e IA64.

+0

El TSC a veces puede no ser confiable, especialmente en sistemas multinúcleo. Consulte http://en.wikipedia.org/wiki/Time_Stamp_Counter para obtener más detalles. –

7

Si tiene código de tiempo, vale la pena ejecutarlo muchas veces en un bucle para evitar el efecto de la resolución del temporizador. Entonces, puede ejecutar lo que está cronometrando 10.000 veces y medir la cantidad de tiempo que lleva ejecutar todas las iteraciones. Probablemente solo tome unos segundos para ejecutarse y obtendrá mejores datos de tiempo.

+1

La única dificultad con esto puede ser que el índice de referencia calienta completamente los "cachés", mientras que el código normalmente se ejecuta en cachés fríos. Pero medir el rendimiento de la memoria fría es difícil. –

+0

Sí, a menos que esté midiendo el rendimiento de la memoria caché, deberá desactivar las memorias caché para realizar pruebas de rendimiento adecuadas. –

+0

¿No deshabilita las cachés de su CPU para dar resultados engañosos en la otra dirección? En lugar de no saber cuándo perderías el rendimiento para cachear errores, ahora no sabes cuándo obtendrás rendimiento por éxitos de caché. –

5

Usar "el número de operaciones" es una mala idea cuando se piensa en el rendimiento. No tiene en cuenta las diferencias entre los recuentos del mejor y peor caso de ciclo para cada operación, los costos de fallas de caché, fallas en la tubería, paralelización potencial (automática), etc.

Como dice Greg, generalmente es una mejor idea de que un microbenchmark simplemente ejecute el mismo código las veces suficientes para obtener un lapso de tiempo decente.

Aún mejor es ejecutar toda su aplicación con una carga de trabajo realista y medir las métricas que está realmente interesado en, pero eso es un asunto diferente ...

Lo es ciertamente útil es trabajar en el complejidad de su código - sepa cuándo un método va a ser O (1), O (log n), O (n) etc. Eso normalmente no implica conocer los detalles de lo que hacen las instrucciones individuales en C++ - aunque haga, necesita saber la complejidad de todo lo que llame. (La historia de Joel de Shlemiel the Painter and strlen es el ejemplo más obvio).

0

Use un temporizador de mayor resolución.

3

Generar las operaciones de ensamblaje y recuento. Luego revise los ciclos/op que usa su procesador. Luego recuerde que está trabajando en un sistema operativo preventivo y nada de eso es válido.

Más en serio, suba su n y amplíe su programa a tamaños obscenos. Eso te dará una idea de cuál es la velocidad de tu programa.

2

Si desea un recuento de operación real proveniente de su hardware, entonces puede considerar instalar un paquete como PAPI - Performance API, que funciona en muchas combinaciones diferentes de sistema operativo y procesador. Utiliza contadores de hardware reales e informa de valores directos o derivados para una gran cantidad de métricas de rendimiento diferentes, como Total Ops, FLOPS, aciertos/errores de caché, etc. También puede dar acceso a temporizadores de mayor resolución.

No es el paquete más fácil de la historia, pero el nivel de informes realmente puede ayudarlo a analizar el comportamiento de su aplicación en su hardware.

3

Utilice valgrind en Linux. Tiene tiempo de nivel de instrucción, incluido el análisis de caché.

0

¿Por qué no solo ejecuta su código en un generador de perfiles? Normalmente, esto le proporciona datos sobre cuánto tiempo se dedica a las funciones y cuántas veces se las llama.

Saber cuántas veces se llama a una función es útil porque puede permitir detectar posibles problemas de rendimiento si se llama a una función mucho más a menudo de lo que cree que debería ser.

Por supuesto, el uso de un generador de perfiles hace que su código sea más lento, pero eso es inevitable al agregar cualquier tipo de instrumentación.

0

Si desea una sincronización precisa (en Windows) sin utilizar un generador de perfiles, puede consultar this thread, que presenta diferentes formas de crear perfiles de código C++.

Cuestiones relacionadas