2013-08-16 28 views
5

Ho appena elaborato la documentazione di http://www.gnu.org/software/gettext/manual/gettext.html e non si discute del sovraccarico delle prestazioni. Su Internet, ho trovato solo discussioni sulle prestazioni per altri linguaggi (PHP e Java) ma niente per C/C++.overhead delle prestazioni del sistema di internazionalizzazione gettext in C/C++

Quindi le mie domande:??

  1. Qual è il sovraccarico delle prestazioni durante l'avvio di un programma che utilizza gettext (libreria condivisa di carico Come sono le traduzioni caricati in memoria vengono tutte le traduzioni caricati all'avvio o on- domanda?)

  2. Qual è la penalità delle prestazioni durante il normale funzionamento del programma? (vale a dire quando è necessaria una traduzione) Quanto è grande l'aumento di memoria del programma e come è organizzata la memoria? C'è un pericolo/possibilità maggiori che parti del programma vengano scambiate su disco quando il programma è inattivo? (Se le traduzioni sono memorizzate in una parte molto diversa della memoria rispetto al resto del programma, a mio avviso la possibilità di un errore di pagina è superiore rispetto a una versione non internazionalizzata del programma)

  3. Anche un programma che gira sotto il piano "C" subisce queste penalità per le prestazioni?

Grazie mille.

+0

Un programma che utilizza 'gettext' sta generando output leggibile dall'uomo. Il sovraccarico è meno che ci vorrà l'umano a leggere il testo, quindi può essere considerato trascurabile. (Questo non è assolutamente vero, ma realisticamente, il sovraccarico non è un problema.) –

+0

@JamesKanze il programma potrebbe anche generare rapporti di lunga durata, oppure inviare e-mail di massa personalizzate, oppure ... Che l'output sia leggibile dall'uomo non implica che ci sia un umano intorno e che il programma possa fermarsi fino a quando non ha finito di leggere l'output. – Chris

+0

@James: anche un singolo byte mancante che causa un errore di pagina causerà un ritardo che è evidente anche da un essere umano. In casi estremi può causare un ritardo di alcuni secondi se il disco rigido deve essere attivato. Anche molti programmi da riga di comando vengono avviati da script che possono significare che un programma viene avviato/arrestato migliaia di volte. Ma il vero punto è che "è neglegabile" non è una risposta alle domande poiché in alcuni casi è solo neglegabile. – Robby75

risposta

3

Dato che l'alternativa a questo approccio è quello di avere un gran numero di build, ognuno con qualcosa di simile in esso:

int main() 
{ 
    printf(
#ifdef SWEDISH 
      "Hej världen\n" 
#elsif ENGLISH 
      "Hello, World\n" 
#elsif PORTUGUESE 
      "Olá, Mundo\n" 
#else 
    #error Language not specified. 
#endif 
    ); 
    return 0l; 
} 

invece otteniamo:

int main() 
{ 
    printf(gettext("Hello, World\n")); 
} 

che è facile da leggi e capisci.

Non conosco la struttura esatta dell'implementazione gettext, ma mi aspetto che sia una tabella hash una volta caricata. Forse un albero binario, ma il tavolo hash sembra più ragionevole.

Per quanto riguarda le spese generali esatte, è molto difficile mettere un numero su di esso - specialmente, come dici tu, se qualcosa viene scambiato su disco, e il disco si è fermato, ci vogliono 3-4 secondi per sollevare il disco accelerare. Quindi come lo si quantifica? Sì, è possibile che la pagina necessaria per gettext venga sostituita se il sistema è occupato a fare qualcosa di intensivo della memoria.

Il caricamento del file di messaggi deve essere solo un grande sovraccarico se il file è molto grande, ma ancora, se il disco non gira e il file non viene memorizzato nella cache, ci sarà un sovraccarico di diversi secondi. Ancora una volta, come quantificarlo. La dimensione del file è chiaramente direttamente proporzionale alla dimensione effettiva dei messaggi tradotti (o madrelingua).

quanto riguarda il punto 2:

Per quanto ne so, sia in Linux e Windows, le pagine vengono scambiati su un "usato meno di recente" (o qualche altro uso statistico) Base, che non ha nulla a che fare con dove si trovano. Chiaramente i messaggi tradotti si trovano in un posto diverso dal codice reale - non c'è un elenco di 15 traduzioni diverse nel file sorgente, quindi le traduzioni vengono caricate in fase di runtime e si troveranno in un posto diverso dal codice stesso.Tuttavia, l'overhead di questo è simile alla differenza tra le spese generali:

static const char *msg = "Hello, World\n"; 

e

static const char *msg = strdup("Hello, World\n"); 

Dato che il testo-stringhe sono generalmente tenuti insieme nel binario di un programma in ogni caso, non lo faccio pensare che la loro "vicinanza" al codice in esecuzione sia significativamente diversa da un pezzo di memoria allocata dinamicamente da qualche parte nell'heap. Se chiami la funzione gettext abbastanza spesso, quella memoria verrà mantenuta "corrente" e non sostituita. Se non chiami gettext per un po 'di tempo, potrebbe essere scambiato. Ma questo vale per "nessuna delle stringhe memorizzate nell'eseguibile è stata usata di recente, quindi sono state scambiate".

3) Penso che l'inglese (o "nessuna lingua selezionata") sia trattato esattamente identico a qualsiasi altra variante linguistica.

avrò un po 'più di scavo in un po', hanno bisogno di colazione prima ...

molto poco scientifica:

#include <libintl.h> 
#include <cstdio> 
#include <cstring> 

static __inline__ unsigned long long rdtsc(void) 
{ 
    unsigned hi, lo; 
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 
    return ((unsigned long long)lo)|(((unsigned long long)hi)<<32); 
} 


int main() 
{ 
    char str[10000] = {}; 
    char *s = str; 
    unsigned long long time; 

    for(int i = 0; i < 10; i++) 
    { 
    time = rdtsc(); 
    s += sprintf(s, "Hello, World %d", i); 
    time = rdtsc() - time; 
    printf("Time =%lld\n", time); 
    } 
    printf("s = %s\n", str); 
    s = str; 

    strcpy(s, ""); 
    for(int i = 0; i < 10; i++) 
    { 
    time = rdtsc(); 
    s += sprintf(s, gettext("Hello, World %d"), i); 
    time = rdtsc() - time; 
    printf("Time =%lld\n", time); 
    } 
    printf("s = %s\n", str); 
} 

dà i seguenti risultati:

$ g++ -Wall -O2 intl.cpp 
$ ./a.out 
Time =138647 
Time =9528 
Time =6710 
Time =5537 
Time =5785 
Time =5427 
Time =5406 
Time =5453 
Time =5644 
Time =5431 
s = Hello, World 0Hello, World 1Hello, World 2Hello, World 3Hello, World 4Hello, World 5Hello, World 6Hello, World 7Hello, World 8Hello, World 9 
Time =85965 
Time =11929 
Time =1
Time =10226 
Time =10628 
Time =9613 
Time =9515 
Time =9336 
Time =9440 
Time =9095 
s = Hello, World 0Hello, World 1Hello, World 2Hello, World 3Hello, World 4Hello, World 5Hello, World 6Hello, World 7Hello, World 8Hello, World 9 

Il codice in dcigettext.c utilizza una combinazione di ricerca binaria in una matrice di stringhe piatte e una funzione di hash che esegue l'hash della stringa su un hash PJW (vedere: http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html).

Quindi, il sovraccarico, una volta avviata l'applicazione, sembra essere "appena percettibile" (quando si contano i clock), ma non enorme.

Il tempo esatto necessario per eseguire il primo sprintf varia leggermente in entrambi i casi, quindi non direi che "utilizzando gettext" rende sprintf più veloce alla prima chiamata - solo "sfortuna" in questa esecuzione (I disponeva di alcune altre varianti del codice e variava notevolmente durante la prima chiamata a sprintf e meno per le chiamate successive). Probabilmente alcune impostazioni (possibilmente cache [printf che causa la sovrascrittura delle cache con altri rifiuti è molto probabile], previsione delle branche, ecc. Da qualche parte che richiede più tempo ...

Ora, questo chiaramente non risponde alle tue domande sul cercapersone fuori, ecc. E non ho provato a fare una traduzione svedese, portoghese o tedesca del mio messaggio "Hello, World". Continuo a credere che non sia enorme, a meno che non si eseguano effettivamente centinaia di istanze di un'applicazione al secondo, e che l'applicazione non faccia molto altro che stampare un messaggio sullo schermo dopo aver fatto alcuni semplici calcoli, certo, potrebbe essere importante .

L'unico vero modo per scoprire quanta differenza fa è compilare la stessa applicazione con #define _(x) x anziché #define _(x) gettext(x) e vedere se si nota alcuna differenza.

Continuo a pensare che "il paginato" sia un'aringa rossa. Se la macchina è sotto la pressione di memoria ALTA, allora sarà in esecuzione lenta, non importa cosa (Se scrivo un pezzo di codice che alloca 16 GB [Ho 16 GB di RAM nella macchina] sulla mia macchina, quasi tutto tranne la tastiera stessa (può lampeggiare il LED del blocco numerico) e il puntatore del mouse stesso (può spostare il puntatore del mouse sullo schermo) non risponde).

+0

Grazie per la risposta; Una tabella hash significherebbe AFAIK che per ogni traduzione, un hash dovrebbe essere creato in fase di esecuzione e l'hash deve essere trovato nella tabella. In tal caso, la tua applicazione "Hello World" verrebbe probabilmente eseguita più volte più lentamente di quanto non faccia a gettext. – Robby75

+0

per quanto riguarda il punto 2: è molto importante dove si trovano. Quando si trovano vicino al programma principale, la possibilità è molto alta che entrambi siano sulla stessa pagina! Un programma distribuito su più pagine causerà molti più errori di pagina rispetto a un programma che utilizza solo una pagina. – Robby75

+0

Sì, ma perché il testo sia sulla stessa pagina del codice richiede che il codice sia molto piccolo (meno di 4KB). Se stiamo parlando di un codice che in realtà fa qualcosa di significativo e utile, al di là della stampa di "Hello, World \ n", è probabile che il codice e il testo coprano più di una singola pagina. Calcolare un hash per una stringa non è del tutto banale, ma più semplice dell'elaborazione che printf fa alla stringa di formato, quindi non credo che tu sia proprio lì. Ma ho fatto colazione, ora guardo cosa fa effettivamente libintl. –

1

Alcune misurazioni:

for (; n > 0; n--) { 
#ifdef I18N 
      fputs(gettext("Greetings!"), stdout); 
#else 
      fputs("Greetings!", stdout); 
#endif 
      putc('\n', stdout); 
    } 

Con n = 10000000 (10 milioni), e l'uscita reindirizzamento a un file. Nessun file po per le impostazioni internazionali, quindi viene stampata la stringa originale (file di output identico). Ora Utente in secondi:

  • 0,23 con I18N indefinito
  • 4.43 con I18N
  • 2.33 con I18N e LC_ALL = C

Overhead di 0,4 microsecondi per chiamata. (Su Phenom X6 a 3.6GHz, Fedora 19). Con LC_ALL = C l'overhead è solo 0,2 μs. Nota che questo è probabilmente il caso peggiore, di solito farai qualcosa di più nel tuo programma. Eppure, è un fattore di 20, e questo include IO. gettext() è piuttosto più lento di quanto mi sarei aspettato.

Utilizzo memoria Non ho misurato, in quanto probabilmente dipende dalla dimensione del file po. Ora di avvio Non ho idea di come misurare.

+0

Forse imposta n su 1 e avvialo in uno script: 'for i in \' seq 10000000 \ '; fai run_test_program; fatto per misurare il tempo di avvio. In realtà, questo scenario è esattamente ciò che viene fatto nel mondo reale molto spesso con strumenti da riga di comando. – Robby75

+0

Un'altra cosa: dici di non avere file-po, questo significherebbe che con un file po l'overhead sarebbe ancora molto più grande perché avrebbe dovuto analizzare/cercare/etc - quindi peggio ancora nel peggiore dei casi; -) – Robby75