En SO, hay bastantes preguntas sobre el perfil de rendimiento, pero no parece encontrar la imagen completa. Hay bastantes problemas involucrados y la mayoría de los Q & A ignoran todos menos unos pocos a la vez, o no justifican sus propuestas.¿La mejor manera de probar la velocidad del código en C++ sin profiler, o no tiene sentido intentarlo?
Lo que me estoy preguntando. Si tengo dos funciones que hacen lo mismo y tengo curiosidad acerca de la diferencia de velocidad, ¿tiene sentido probar esto sin herramientas externas, con temporizadores, o las pruebas compiladas afectarán demasiado los resultados?
Lo pregunto porque si es sensato, como programador de C++, quiero saber cómo se debe hacer mejor, ya que son mucho más simples que usar herramientas externas. Si tiene sentido, sigamos con todas las trampas posibles:
Considere este ejemplo. El siguiente código muestra 2 formas de hacer lo mismo:
#include <algorithm>
#include <ctime>
#include <iostream>
typedef unsigned char byte;
inline
void
swapBytes(void* in, size_t n)
{
for(size_t lo=0, hi=n-1; hi>lo; ++lo, --hi)
in[lo] ^= in[hi]
, in[hi] ^= in[lo]
, in[lo] ^= in[hi] ;
}
int
main()
{
byte arr[9] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
const int iterations = 100000000;
clock_t begin = clock();
for(int i=iterations; i!=0; --i)
swapBytes(arr, 8);
clock_t middle = clock();
for(int i=iterations; i!=0; --i)
std::reverse(arr, arr+8);
clock_t end = clock();
double secSwap = (double) (middle-begin)/CLOCKS_PER_SEC;
double secReve = (double) (end-middle )/CLOCKS_PER_SEC;
std::cout << "swapBytes, for: " << iterations << " times takes: " << middle-begin
<< " clock ticks, which is: " << secSwap << "sec." << std::endl;
std::cout << "std::reverse, for: " << iterations << " times takes: " << end-middle
<< " clock ticks, which is: " << secReve << "sec." << std::endl;
std::cin.get();
return 0;
}
// Output:
// Release:
// swapBytes, for: 100000000 times takes: 3000 clock ticks, which is: 3sec.
// std::reverse, for: 100000000 times takes: 1437 clock ticks, which is: 1.437sec.
// Debug:
// swapBytes, for: 10000000 times takes: 1781 clock ticks, which is: 1.781sec.
// std::reverse, for: 10000000 times takes: 12781 clock ticks, which is: 12.781sec.
Las cuestiones:
- de los temporizadores usar y cómo hacer el tiempo de CPU realmente consumida por el código en la pregunta?
- ¿Cuáles son los efectos de la optimización del compilador (dado que estas funciones simplemente intercambian bytes de ida y vuelta, lo más eficiente es, obviamente, no hacer nada en absoluto)?
- Considerando los resultados presentados aquí, ¿cree que son precisos (puedo asegurarle que múltiples ejecuciones dan resultados muy similares)? En caso afirmativo, puede explicar cómo std :: reverse llega a ser tan rápido, teniendo en cuenta la simplicidad de la función personalizada. No tengo el código fuente de la versión vC++ que utilicé para esta prueba, pero here is the implementation de GNU. Todo se reduce a la función iter_swap, que es completamente incomprensible para mí. También se espera que esto se ejecute dos veces más rápido que esa función personalizada, y si es así, ¿por qué?
contemplaciones:
parece se proponen dos contadores de tiempo de alta precisión: clock() y QueryPerformanceCounter (en Windows). Obviamente, nos gustaría medir el tiempo de CPU de nuestro código y no el tiempo real, pero por lo que yo entiendo, estas funciones no dan esa funcionalidad, por lo que otros procesos en el sistema interferirían con las mediciones. This page en la biblioteca gnu c parece contradecir eso, pero cuando pongo un punto de interrupción en vC++, el proceso depurado obtiene una gran cantidad de marcas de reloj a pesar de que se suspendió (no he probado en gnu). ¿Me faltan contadores alternativos para esto o necesitamos al menos bibliotecas o clases especiales para esto? Si no es así, ¿el reloj es lo suficientemente bueno en este ejemplo o habría una razón para usar QueryPerformanceCounter?
¿Qué podemos saber con certeza sin herramientas de depuración, desensamblaje y creación de perfiles? ¿Está pasando algo realmente? ¿La llamada a la función está en línea o no? Al verificar el depurador, los bytes realmente se intercambian, pero prefiero saber por la teoría por qué, que a partir de las pruebas.
Gracias por cualquier instrucción.
actualización
Gracias a una hint de tojas la función swapBytes ahora corre más rápido que el std :: inversa. No me había dado cuenta de que la copia temporal en caso de un byte debe ser solo un registro, y por lo tanto es muy rápido. La elegancia puede cegarte.
inline
void
swapBytes(byte* in, size_t n)
{
byte t;
for(int i=0; i<7-i; ++i)
{
t = in[i];
in[i] = in[7-i];
in[7-i] = t;
}
}
Gracias a una tip de ChrisW he encontrado que en las ventanas se puede obtener el tiempo real de la CPU consumidos por una (es decir: tu) proceso a través Windows Management Instrumentation. Esto definitivamente parece más interesante que el contador de alta precisión.
¿Qué SO estás preguntando? Cuando escribí el código de tiempo, los diversos sistemas operativos tenían llamadas API diferentes para el reloj correcto. –
Estoy probando en WindowsXP, pero sería igualmente interesante escuchar acerca de otros SO's – nus
que vale la pena probar sin un generador de perfiles, después de probar con un generador de perfiles. –