2012-08-06 10 views
5

Estoy en una situación en la que después de haber analizado y perfilado nuestro sistema, llegamos a la conclusión de que el componente de registro del sistema es uno de los muchos cuellos de botella que aproximadamente ~ 17% del tiempo de ejecución total - se registran muchas cosas.Sello de fecha/hora eficientemente calculado para fines de registro en unix/win32

De esto, aproximadamente el 5% del tiempo que consume el registrador está relacionado con la producción de una marca de fecha/hora en ascii en el siguiente formato: AAAAMMDD HHMMSS.fff - registramos aproximadamente 700k líneas por segundo. (aproximadamente 700K x (tiempo local y gettimeofday) llamadas por segundo)

Me preguntaba qué técnicas tienen los compañeros SOERS para producir sellos de tiempo de manera eficiente.

Las soluciones de plataforma cruzada serían bienvenidas.

Nota1: examinamos Boost.datetime - es genial, pero un poco lento para nuestras necesidades, std :: chrono es una solución perfecta, pero desafortunadamente tenemos que admitir compiladores anteriores a C++ 11.

Nota2: Hemos implementado una optimización simple que solo computa la parte de la fecha (aaaammdd) una por 24 horas, por lo tanto, solo tiene 1 llamada gettimeofday por línea, aunque no ayudó mucho.

+1

¿El formato * que ocupa solo el 5% o incluye las otras llamadas para recuperar la hora? (Aunque, incluso si el 5% se convirtiera en 0%, seguiría siendo como ~ 16.7% en total :-) –

+2

@pst: estas son solo las llamadas para rellenar las distintas estructuras de tiempo. el formateo (conversión a ascii) es otro problema por completo. –

+0

@pst: pero cualquier idea para hacer el formateo sería grandiosa, ya usamos luts de ascii combos para acelerar el proceso ej .: 01020304050607080910111213141516171819202122232425262728293031 ¿algo como esto en mente? –

Respuesta

3

Si tiene la opción de utilizar C++ 11, debe consultar std::chrono.

De lo contrario, la optimización dependerá de la resolución que necesite. Le pregunto si necesita marcas de tiempo en el registro o si las indicaciones de tiempo ocasionales con información de secuencia pueden ser útiles.

Ejemplo:

<timestamp1> <seq_num_0> ... 
<timestamp1> <seq_num_1> ... 
.... 
<timestamp1> <seq_num_n-1> ... 
<timestamp2> <seq_num_0> ... 

La forma en que lo veo, tiene dos problemas:

  1. Sincronización de las marcas de tiempo con otros sistemas
  2. Obtención de una marca de tiempo precisa en un único sistema

Usaría un sistema basado en temporizador para actualizar la marca de tiempo dos veces cada milisegundo y volver a usarlo entre las actualizaciones. Entonces me aseguraré de que los sistemas en los que se ejecuta el código tengan sus relojes sincronizados con un reloj atómico. Generas la marca de tiempo dos veces para tratar de compensar la falta de coherencia de los mecanismos del temporizador del sistema operativo subyacente.

No creo que pueda obtener mucho mejor que eso.

EDITAR: En realidad, puedes. Asegúrese de formatear solo la cadena de marca de tiempo cuando cambie. Tampoco necesita un número de secuencia, si puede garantizar que las entradas se registran en el orden en que ingresan. Dadas esas dos suposiciones, su problema de registro ahora se reduce a la velocidad con la que puede concatenar y escribir dos cadenas.

ACTUALIZACIÓN 2: si el refuerzo no es adecuado y si no se puede utilizar C++ 11, que se reduce a esto:

  1. utilizar un temporizador para establecer y dar formato a la fecha y hora dos veces cada milisegundo - puedes hacerlo a través de las API del nivel del sistema operativo.
  2. Asegúrate de que los eventos se registran en el orden en que entran.

Asumiendo que la E/S no es su cuello de botella, su problema no es más que una concatenación de cadenas rápida.

+2

Lea la nota 1. –

+1

Los sellos de tiempo son críticos ya que los usamos para coincidir con eventos que ocurren en otros sistemas. –

+1

@Zamfir: ¿Y qué tal [Boost.Chrono] (http://www.boost.org/libs/chrono/)? – ildjarn

-1

Edit: Varios downvoters ahora. Por favor, deje un comentario para que pueda abordar los problemas correctamente. ¡Gracias!

Puede reorganizar su código para que su registrador lea una cadena de fecha y hora de un búfer que se actualiza N veces por segundo (dependiendo de la resolución que desee) mediante algún otro hilo. Para 4 veces por segundo:

struct current_time_stamp { 
    char timestr_[4][16]; 
    unsigned index_; 
    unsigned subsecond_; 
    const char *get() const { return timestr_[index_%4]; } 
    void update() { 
     // ... update string in timestr_[(index_+1)%4] ... 
     // ... if (index_ + 1)%4 is zero, recompute subsecond_ 
     ATOMIC_INCREMENT(index_); 
     // ... also need a memory barrier for timestr_ update 
    } 
}; 

La resolución por segundo de cada registro se leerá desde un contador de alto rendimiento. DeadMG sugiere QueryPerformanceTimer en windows, y en Linux (y POSIX) hay clock_gettime. Sin embargo, si la sobrecarga de esas implementaciones aún es demasiado alta para usted, puede consultar el contador de marca de tiempo en el procesador directamente utilizando el ensamblaje en línea (consulte rdtsc para x86). El valor de subsegundo se delta'd del registrado en la estructura para obtener el desplazamiento correcto.

Si puede salirse con la suya registrando la marca de tiempo en un formato binario, eso se alejaría del problema de formateo.

+1

¿Por qué no usaría simplemente un contador regular de alto rendimiento, como 'QueryPerformanceCounter'? No hay ninguna razón para bajar al ensamblador en línea aquí. – Puppy

+2

@DeadMG: La pregunta era sobre el rendimiento. No tengo experiencia en usar 'QueryPerformanceCounter' en Windows, pero usar' rdtsc' directamente en Linux supera las llamadas directas a 'clock_gettime' en 100x. – jxh

+0

Es completamente específico para su implementación, su compilador y sus circunstancias. Necesitaría datos de perfil para probar esto para el OP. – Puppy

0

Me inmediatamente cualquier y todo el formato hasta que realmente se necesitan:

struct log_entry { 
    struct timeval timestamp; 
    unsigned int code; 
    union { 
     struct param1 p1; 
     struct param2 p2; 
    }; 
}; 

Los paramN estructuras contienen los datos apropiados para el evento en cualquier forma que se encontraba en ese momento, sino como una copia (por lo que el los datos de registro se pueden analizar de forma independiente).

Dependiendo de sus requisitos, puede guardar estos datos en un buffer de anillo y sobrescribir constantemente los datos viejos, o volcarlos en el disco cuando se alcanza un cierto porcentaje.

+0

+1 - eso es básicamente lo que haría dado el desagradable requisito del OP para la velocidad de registro. Cree una matriz de 1M de estos elementos para actuar como un búfer circular y luego cada, digamos 100 ms, emita índices de inicio/finalización en el hilo del registrador, quizás también con el tiempo de pared si el miembro de marca de tiempo solo contiene conteos de ticks (o lo que devuelve la API más rápida relacionada con el tiempo en cualquier sistema operativo). El hilo del registrador puede formatear, agregar compensaciones a la pared, interpolar, etc. en pedazos grandes y agradables. –

Cuestiones relacionadas