2013-10-11 3 views
5

Abbiamo un codice che vuole chiamare lo localtime molto spesso da più thread. (Sfondo pertinente: è un server in cui una delle cose che puoi chiedere è l'ora locale come stringa, e vuole essere in grado di servire 100Ks di richieste al secondo.)API Linux/crossplatform per le regole del fuso orario? (sostituire il blocco localtime_r)

Abbiamo scoperto che su Ubuntu Linux 12.04, la funzione di glibc localtime_r ("localtime rientrante") chiama __tz_convert, che still takes a global lock!

(Inoltre, sembra che FreeBSD rende localtime_r chiamata tzset su ogni singola chiamata, perché sono paranoico che il programma avrebbe potuto fare un setenv("TZ") e/o l'utente ha scaricato una nuova versione di /etc/localtime da ora e l'ultima volta è stato chiamato localtime_r (questo è l'opposto del sit descrizione descritta here; sembra che glibc chiama tzseton every invocation of localtime but not localtime_r, solo per essere fonte di confusione.)

Ovviamente, questo è terribile per le prestazioni. Per i nostri scopi, vorremmo fondamentalmente "scattare" le regole per il nostro fuso orario corrente quando il server inizia a funzionare, e quindi usare quella istantanea per sempre. Quindi continueremo a rispettare le regole di Daylight Savings Time (perché le regole per quando passare a DST farebbero parte dello snapshot), ma non verremmo mai tornare sul disco, prendere mutex o fare qualsiasi altra cosa che causerebbe thread bloccare. (Non stiamo rispettando gli aggiornamenti scaricati su tzinfo e non rispettando le modifiche allo /etc/localtime, non ci aspettiamo che il server cambi fisicamente i fusi orari mentre è in esecuzione.)

Tuttavia, non riesco a trovare alcuna informazione online su come per gestire le regole del fuso orario - se esiste un'API userspace per lavorare con loro, o se saremo costretti a reimplementare alcune centinaia di righe di codice glibc per leggere da soli i dati del fuso orario.

Dobbiamo reimplementare tutto a valle di __tz_convert - incluso tzfile_read, poiché non sembra essere esposto agli utenti? O c'è qualche interfaccia POSIX e/o libreria di terze parti che potremmo usare per lavorare con le regole del fuso orario?

(ho visto http://www.iana.org/time-zones/repository/tz-link.html ma non sono sicuro che sia utile.)

+0

Non è affatto ovvio che il blocco globale in 'tzset' è" terribile per le prestazioni "- lo hai effettivamente confrontato? A meno che non ti piaccia, sembra essere il caso della proverbiale ottimizzazione prematura. Per quanto ne so, 'tzset' ha un percorso veloce che sostanzialmente non fa nulla se il fuso orario non è effettivamente cambiato. In un'applicazione reale mi aspetterei che il blocco non venga mai discusso. – user4815162342

+0

Il blocco viene discusso ogni volta che due thread chiamano '__tz_convert' in una volta; controlla il codice E sì, l'unica ragione per cui sappiamo che questo blocco esiste è che abbiamo visto che si trattava di un collo di bottiglia nei test di perf (test specifici che servivano molte richieste locali). – Quuxplusone

+1

Hai guardato [Boost] (http://www.boost.org/doc/libs/1_54_0/doc/html/date_time.html) o [ICU] (http://userguide.icu-project.org/ appuntamento)? –

risposta

2

Usa https://github.com/google/cctz

E 'veloce e dovrebbe fare tutto quello che vuoi con un semplice API.

In particolare, per un cctz equivalente a localtime, utilizzare la funzione cctz::BreakTime(). Ad esempio, https://github.com/google/cctz/blob/master/examples/example3.cc

+2

In genere, i collegamenti a uno strumento o una libreria [devono essere accompagnati da note sull'utilizzo, una spiegazione specifica di come la risorsa collegata è applicabile al problema o di qualche codice di esempio] (http://meta.stackoverflow.com/a/251605), o se possibile tutto quanto sopra. – IKavanagh

+0

Grazie, Greg. Quella libreria sembra fantastica. C'è anche un blocco globale nella tua libreria, vedi https://github.com/google/cctz/blob/master/src/time_zone_impl.cc#L64. Trovato da Cloudera come descritto nei commenti su https://issues.cloudera.org/browse/IMPALA-3316. Inviato https://github.com/google/cctz/issues/23 – Tagar

0

Forse questo free, open-source timezone library si adatta alle necessità.

Ha un flag di configurazione chiamato LAZY_INIT che è completamente documentato here. Per impostazione predefinita è attivo e causerà chiamate a std::call_once la prima volta che si accede a ogni singolo fuso orario. Tuttavia è possibile compilare con:

-DLAZY_INIT=0 

e poi le chiamate a std::call_once andare via. Ogni singolo fuso orario viene letto dal disco e completamente inizializzato al primo accesso (tramite una funzione locale statica). Da quel momento in poi, le cose sono stabili e senza blocco, senza accesso al disco. Naturalmente questo aumenta i tempi di inizializzazione in anticipo, ma diminuisce il tempo di "primo accesso" per ciascun fuso orario.

Questa libreria richiede C++ 11/14, pertanto potrebbe non essere adatta per questo motivo. È basato su (e utilizza intensivamente) la libreria C++ 11 <chrono>. Ecco il codice di esempio che stampa l'ora locale corrente:

#include "tz.h" 
#include <iostream> 

int 
main() 
{ 
    using namespace date; 
    auto local = make_zoned(current_zone(), std::chrono::system_clock::now()); 
    std::cout << local << '\n'; 
} 

che ha appena uscita per me:

2016-04-12 10:13:14.585945 EDT 

La biblioteca è un filo ad alte prestazioni di design moderno, sicuro. È anche molto flessibile e completamente documentato. Le sue capacità vanno ben oltre la semplice sostituzione di C's localtime. A differenza l'API C, è possibile specificare qualsiasi IANA timezone è necessario, ad esempio:

auto local = make_zoned("Europe/London", std::chrono::system_clock::now()); 

Questo dà il tempo corrente a Londra:

2016-04-12 15:19:59.035533 BST 

Nota che di default la precisione del timestamp è quello di std::chrono::system_clock. Se si preferisce un altro precisione, che è facilmente realizzabile:

using namespace date; 
using namespace std::chrono; 
auto local = make_zoned("Europe/London", std::chrono::system_clock::now()); 
std::cout << format("%F %H:%M %Z", local) << '\n'; 

2016-04-12 15:22 BST 

Vedere la docs per maggiori dettagli.