2012-08-06 1 views
5

Sono in una situazione in cui dopo aver analizzato e analizzato il nostro sistema, giungere alla conclusione che il componente di registrazione del sistema è uno dei molti colli di bottiglia circa il ~ 17% del tempo di esecuzione totale: molte cose vengono registrate.Data e ora di calcolo efficiente per scopi di registrazione su unix/win32

Di questo, circa il 5% delle volte che il logger consuma è correlato alla produzione di un timbro data/ora in ascii nel seguente formato: AAAAMMGG HHMMSS.fff - registriamo approssimativamente circa 700k linee al secondo. (circa 700K x (ora locale e gettimeofday) chiamate al secondo)

Mi chiedevo quali tecniche hanno i colleghi SO per produrre timestamp in modo efficiente.

Le soluzioni multipiattaforma sarebbero benvenute.

Nota1: abbiamo esaminato Boost.datetime: è grandioso, ma un po 'troppo lento per le nostre esigenze, std :: chrono è una soluzione perfetta, tuttavia dobbiamo purtroppo supportare i compilatori di pre C++ 11.

Nota 2: Abbiamo implementato una semplice ottimizzazione che calcola solo la parte data (aaaammgg) uno per 24 ore, quindi, solo con 1 gettimeofday chiamata per linea - non ha aiutato molto però.

+1

La * formattazione * occupa solo il 5% o include le altre chiamate per recuperare il tempo? (Anche se il 5% è stato convertito allo 0%, sarebbe comunque pari al ~ 16,7% totale :-) –

+2

@pst: queste sono solo le chiamate per popolare le varie strutture temporali. la formattazione (conversione in ascii) è un altro problema. –

+0

@pst: ma qualsiasi idea per fare la formattazione sarebbe ottima, già usiamo le lute delle combo ascii per accelerare il processo ad esempio: 01020304050607080910111213141516171819202122232425262728293031 qualcosa come questo in mente? –

risposta

3

Se si ha la possibilità di utilizzare C++ 11, è necessario verificare std::chrono.

In caso contrario, l'ottimizzazione dipenderà dalla risoluzione necessaria. Ti chiederei se hai assolutamente bisogno di timestamp sulla registrazione o se i timestamp occasionali con informazioni sulla sequenza potrebbero essere utili?

Esempio:

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

Il mio modo di vedere, si hanno due problemi:

  1. sincronizzare il timestamp con altri sistemi
  2. Ottenere una temporizzazione accurata su un unico sistema

Vorrei utilizzare un sistema basato su timer per aggiornare il timestamp due volte ogni millisecondo e riutilizzarlo tra gli aggiornamenti. Mi assicurerei quindi che i sistemi su cui gira il codice abbiano i loro orologi sincronizzati su un orologio atomico. Si genera il timestamp due volte per cercare di compensare l'instabilità dei meccanismi del timer del sistema operativo sottostante.

Non credo che si possa ottenere molto meglio di così.

MODIFICA: In realtà, è possibile. Assicurati di formattare la stringa di timestamp solo quando cambia. Inoltre, non è necessario un numero di sequenza, se è possibile garantire che le voci vengano registrate nell'ordine in cui entrano. Considerati questi due presupposti, il problema di registrazione si riduce a quanto velocemente è possibile concatenare e scrivere due stringhe.

UPDATE 2: Se BOOST non è adatto e se non è possibile utilizzare C++ 11, tutto si riduce a questo:

  1. utilizzare un timer per impostare e formattare il timestamp due volte ogni millisecondo - puoi farlo tramite API a livello di sistema operativo.
  2. Assicurarsi che gli eventi siano registrati nell'ordine in cui entrano.

Supponendo che I/O non sia il collo di bottiglia, il problema non è altro che una concatenazione di stringhe veloci.

+2

Leggere la nota 1. –

+1

I timestamp sono fondamentali poiché li utilizziamo per associare eventi che si verificano su altri sistemi. –

+1

@Zamfir: E che dire di [Boost.Chrono] (http://www.boost.org/libs/chrono/)? – ildjarn

-1

Modifica: Più downvoters ora. Si prega di lasciare un commento, in modo da poter affrontare correttamente i problemi. Grazie!

È possibile riorganizzare il codice in modo che il registratore stia leggendo una stringa data/ora da un buffer che viene aggiornato N volte al secondo (in base alla risoluzione desiderata) da qualche altro thread. Per 4 volte al secondo:

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 sottoseconda risoluzione per ciascun registro veniva letta da un contatore ad alte prestazioni. DeadMG suggerisce QueryPerformanceTimer su Windows, e su Linux (e POSIX) c'è clock_gettime. Se, tuttavia, l'eccesso di tali implementazioni è ancora troppo alto per te, puoi interrogare il contatore timestamp sul processore direttamente usando l'assembly inline (vedi rdtsc per x86). Il valore di sottosecondo è delta'd da quello registrato nella struttura per ottenere l'offset corretto.

Se si riesce a superare la registrazione della data e ora in un formato binario, ciò si risolverebbe dal problema di formattazione.

+1

Perché non dovresti semplicemente usare un normale contatore ad alte prestazioni, come 'QueryPerformanceCounter'? Non c'è motivo di scendere all'assemblatore in linea qui. – Puppy

+2

@DeadMG: la domanda riguardava le prestazioni. Non ho esperienza con l'uso di 'QueryPerformanceCounter' su Windows, ma l'uso di' rdtsc' direttamente su Linux ha battuto le chiamate dirette a 'clock_gettime' di 100x. – jxh

+0

È completamente specifico per l'implementazione, il compilatore e le circostanze. Avresti bisogno dei dati del profilo per dimostrarlo per l'OP. – Puppy

0

avrei ritardare e tutta la formattazione fino effettivamente necessari:

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

Le paramN strutture contengono l'appropriata dati per l'evento, in qualsiasi forma che erano in quel momento, ma come una copia (in modo che il i dati di log possono essere analizzati autonomamente).

A seconda delle esigenze, è possibile conservare questi dati in un buffer circolare e sovrascrivere costantemente i vecchi dati o scaricarli su disco quando viene raggiunta una determinata percentuale.

+0

+1 - questo è fondamentalmente quello che farei dato il brutto requisito dell'OP per la velocità di registrazione. Crea un array di 1M di queste cose per agire come un buffer circolare e quindi ogni, ad esempio 100ms, emette gli indici di inizio/fine sul thread del logger, magari anche con il wall-time se il membro timestamp contiene solo tick-counts, (o qualsiasi cosa venga restituita dall'API più veloce relativa al tempo su qualsiasi sistema operativo). Il thread del logger può eseguire la formattazione, aggiungere offset al tempo di parete, interpolazione ecc. In parti belle e grandi. –