C ++ tiempo granular fino

El siguiente fragmento de código da 0 como tiempo de ejecución de la función. ¿Alguien puede señalar el error?

struct timeval start,end; long seconds,useconds; gettimeofday(&start, NULL); int optimalpfs=optimal(n,ref,count); gettimeofday(&end, NULL); seconds = end.tv_sec - start.tv_sec; useconds = end.tv_usec - start.tv_usec; long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; cout<<"\nOptimal Runtime is "<<opt_runtime<<"\n"; 

Obtengo la hora de inicio y finalización de la misma. Obtengo la siguiente salida

 Optimal Runtime is 0 

Dime el error por favor.

POSIX 1003.1b-1993 especifica interfaces para clock_gettime() (y clock_getres() ), y ofrece que con la opción MON puede haber un tipo de reloj con un valor CLOCK_MONOTONIC de CLOCK_MONOTONIC (para que su temporizador no se vea afectado por la hora del sistema) ajustes). Si está disponible en su sistema, estas funciones devuelven una estructura que tiene una resolución potencial de hasta un nanosegundo, aunque la última función le dirá exactamente qué resolución tiene el reloj.

  struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* and nanoseconds */ }; 

Es posible que aún deba ejecutar su función de prueba en un bucle varias veces para que el reloj se registre cada vez que transcurra más allá de su resolución, y quizás desee ejecutar su bucle lo suficiente para durar al menos un orden de magnitud más tiempo que el resolución del reloj.

Sin embargo, tenga en cuenta que, aparentemente, los usuarios de Linux no leen las especificaciones POSIX.1b y / o no entendieron la definición de un reloj de tiempo monótonamente creciente, y su reloj CLOCK_MONOTONIC se ve afectado por los ajustes de hora del sistema, por lo que tiene que usar su no inventada. – reloj estándar CLOCK_MONOTONIC_RAW para obtener un reloj monotónico real.

Alternativamente, se podría usar la timer_settime() POSIX.1 timer_settime() para configurar un temporizador en funcionamiento, un manejador de señales para capturar la señal entregada por el temporizador, y timer_getoverrun() para averiguar cuánto tiempo transcurrió entre la puesta en cola de la señal y su entrega final, y luego configure su bucle para que se ejecute hasta que se apague el temporizador, contando el número de iteraciones en el intervalo de tiempo establecido, más el exceso.

Por supuesto, en un sistema multitarea preventivo, estos relojes y temporizadores funcionarán incluso cuando el proceso no se esté ejecutando, por lo que no son realmente muy útiles para la evaluación comparativa.

Un poco más raro es el valor opcional POSIX.1-1999 clockid_t de CLOCK_PROCESS_CPUTIME_ID , indicado por la presencia de _POSIX_CPUTIME de , que representa el reloj de tiempo de CPU del proceso de llamada, dando valores que representan la cantidad de tiempo de ejecución del proceso de invocación. (Aún más rara es la opción TCT de clockid_t de CLOCK_THREAD_CPUTIME_ID , indicada por la macro _POSIX_THREAD_CPUTIME , que representa el tiempo de la CPU, que proporciona valores que representan la cantidad de tiempo de ejecución del subproceso de invocación).

Desafortunadamente, POSIX no menciona si estos llamados relojes CPUTIME cuentan solo con el tiempo del usuario, o el tiempo del usuario y del sistema (y la interrupción), acumulados por el proceso o el subproceso, por lo que si su código de perfil realiza las llamadas al sistema, entonces la cantidad de el tiempo empleado en el modo kernel puede, o no, estar representado.

Peor aún, en sistemas multiprocesador, los valores de los relojes CPUTIME pueden ser completamente falsos si su proceso pasa a migrar de una CPU a otra durante su ejecución. Los temporizadores que implementan estos relojes CPUTIME también pueden funcionar a diferentes velocidades en diferentes núcleos de CPU y en diferentes momentos, lo que complica aún más su significado. Es decir, pueden no significar nada relacionado con el tiempo real del reloj de pared, pero solo es una indicación del número de ciclos de la CPU (lo que puede ser útil para la evaluación comparativa siempre que se utilicen siempre los tiempos relativos y el usuario sea consciente de que el tiempo de ejecución puede variar dependiendo de factores externos). Peor aún, se ha informado que en los relojes CPUTIME basados ​​en TimeStampCounter basados ​​en la CPU de Linux pueden incluso informar el tiempo que ha durado un proceso.

Si su sistema tiene un buen funcionamiento del sistema getrusage() , es de esperar que pueda proporcionarle una struct timeval para cada uno de los tiempos reales del usuario y del sistema consumidos por separado por su proceso mientras se estaba ejecutando. Sin embargo, dado que esto te devuelve a un reloj de microsegundos en el mejor de los casos, tendrás que ejecutar tu código de prueba varias veces para obtener una sincronización más precisa, llamando a getrusage() una vez antes del bucle, y luego nuevamente, y calculando las diferencias. entre los tiempos dados. Para algoritmos simples, esto podría significar ejecutarlos millones de veces, o más. Tenga en cuenta también que en muchos sistemas, la división entre el tiempo del usuario y el tiempo del sistema se realiza de manera un tanto arbitraria y, si se examina por separado en un ciclo repetido, uno o el otro puede parecer que se ejecuta hacia atrás. Sin embargo, si su algoritmo no realiza llamadas al sistema, sumr el tiempo delta debe ser un tiempo total justo para la ejecución de su código.

Por cierto, tenga cuidado al comparar los valores de tiempo de modo que no se desborden o terminen con un valor negativo en un campo, ya sea como sugiere @Nim, o quizás de esta manera (de ):

  #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) 

(incluso es posible que desee ser más paranoico que tv_usec está dentro del rango)

Una nota más importante sobre la evaluación comparativa: asegúrese de que realmente se esté llamando a su función, idealmente examinando la salida del ensamblaje de su comstackdor. La comstackción de su función en un módulo fuente separado del bucle del controlador generalmente convence al optimizador para mantener la llamada. Otro truco es que devuelva un valor que asigne dentro del bucle a una variable definida como volatile .

Tienes una mezcla rara de flotadores y entradas aquí:

 long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; 

Trate de usar:

 long opt_runtime = (long)(seconds * 1000 + (float)useconds/1000); 

De esta manera obtendrás tus resultados en milisegundos.

El tiempo de ejecución de optimal(...) es menor que la granularidad de gettimeofday(...) . Esto probablemente sucede en Windows. En Windows la granularidad típica es de hasta 20 ms. He respondido una pregunta relacionada con gettimeofday (…) aquí . Para Linux, pregunté ¿Cómo se obtiene el microsegundo tiempo de Linux gettimeofday () y cuál es su precisión? Y obtuve un buen resultado.

Más información sobre cómo obtener un tiempo preciso se describe en esta respuesta SO.

Normalmente hago un cálculo como:

 long long ss = start.tv_sec * 1000000LL + start.tv_usec; long long es = end.tv_sec * 1000000LL + end.tv_usec; 

Entonces haz una diferencia

 long long microsec_diff = es - ss; 

Ahora convierte según sea necesario:

 double seconds = microsec_diff / 1000000.; 

Normalmente, no me molesto con el último paso, hago todos los tiempos en microsegundos.