2013-08-16 12 views
5

Właśnie przejrzałem dokumentację http://www.gnu.org/software/gettext/manual/gettext.html i nie ma żadnej dyskusji na temat ogólnego obciążenia wydajności. W Internecie znalazłem tylko dyskusje dotyczące wydajności dla innych języków (PHP i Java), ale nic dla C/C++.Obciążenie wydajności systemu internacjonalizacji gettext w C/C++

Dlatego moje pytania:?

  1. Co jest napowietrznych wydajność podczas uruchamiania programu, który korzysta z gettext (udostępnionej biblioteki obciążenia Jak tłumaczenia załadowany do pamięci wszystkie tłumaczenia są ładowane przy starcie lub on- popyt?)

  2. Jaka jest kara za wyniki podczas normalnej pracy programu? (tzn. kiedy tłumaczenie jest potrzebne) Jak duży jest wzrost ilości pamięci w programie i jak zorganizowana jest pamięć? Czy istnieje większe niebezpieczeństwo/możliwość, że części programu są zamieniane na dysk, gdy program jest bezczynny? (Jeśli tłumaczenia są przechowywane w zupełnie innej części pamięci niż reszta programu, to w moim rozumieniu prawdopodobieństwo błędu strony jest wyższe niż w przypadku wersji nie-umiędzynarodowionej)

  3. Czy program działający pod "C" -locale również podlega tym karom wydajności?

Wielkie dzięki.

+0

Program wykorzystujący 'gettext' generuje dane wyjściowe czytelne dla człowieka. Narzut jest mniejszy niż potrzeba, aby człowiek przeczytał tekst, więc można go uznać za nieistotny. (To nie jest ściśle prawda, ale realistycznie, obciążenie nie jest problemem.) –

+0

@JamesKanze program może również generować długie raporty lub może wysyłać spersonalizowane wiadomości masowe, lub ... To, że dane wyjściowe można odczytać przez człowieka, nie sugerować, że w pobliżu znajduje się człowiek i że program może przestać działać, dopóki nie skończy czytać wyników. – Chris

+0

@ James: Nawet jeden pojedynczy brakujący bajt, który powoduje błąd strony, spowoduje opóźnienie, które jest zauważalne nawet przez człowieka. W skrajnych przypadkach może powodować opóźnienie o kilka sekund, jeśli dysk twardy musi zostać odwirowany. Z skryptów uruchamianych jest również wiele programów uruchamianych z wiersza poleceń, co może oznaczać, że program jest uruchamiany/zatrzymywany tysiące razy. Ale prawdziwym punktem jest to, że "to jest nie do zniesienia" nie jest odpowiedzią na pytania, ponieważ w niektórych przypadkach jest tylko niepotrzebne. – Robby75

Odpowiedz

3

Biorąc pod uwagę, że alternatywą dla tego podejścia jest to, aby mieć dużą liczbę od buduje, każdy z czymś takim w nim:

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; 
} 

zamiast otrzymujemy:

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

który jest łatwy do Przeczytaj i zrozum.

Nie znam dokładnej struktury implementacji gettext, ale spodziewam się, że jest to tablica haszująca po załadowaniu. Prawdopodobnie binarne drzewo, ale tablica hash wydaje się bardziej sensowna.

Co do dokładnych kosztów ogólnych, bardzo trudno jest umieścić na nim numer - szczególnie, jak mówisz, jeśli coś jest zamieniane na dysk, a dysk się zatrzymał, potrzeba 3-4 sekund na podniesienie dysku przyspieszyć. Jak oceniasz to? Tak, możliwe, że strona wymagana dla gettext zostanie zamieniona, jeśli system był zajęty wykonywaniem dużej ilości pamięci.

Ładowanie pliku wiadomości powinno być dużym obciążeniem tylko wtedy, gdy plik jest bardzo duży, ale znowu, jeśli dysk nie obraca się, a plik nie jest buforowany, to będzie narzut kilka sekund. Ponownie, jak to określić. Rozmiar pliku jest wyraźnie wprost proporcjonalny do rzeczywistego rozmiaru przetłumaczonych (lub natywnego języka) wiadomości.

Odnośnie punktu 2:

O ile mi wiadomo, zarówno w systemach Linux i Windows, strony są zamieniane na „najdawniej używane” (lub jakiś inny Wykorzystanie statystycznej) podstawy, która nie ma nic wspólnego z gdzie się znajdują. Oczywiście przetłumaczone wiadomości znajdują się w innym miejscu niż rzeczywisty kod - w pliku źródłowym nie ma listy 15 różnych tłumaczeń, więc tłumaczenia są ładowane w środowisku wykonawczym i będą znajdować się w innym miejscu niż sam kod.Jednakże, narzut ten jest podobny do napowietrznej różnicy między:

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

i

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

Zważywszy, że ciągi tekstowe są na ogół trzymane razem w binarnym programu w każdym razie, nie wiem uważam, że ich "bliskość" kodu wykonującego jest znacząco różna od dynamicznie przydzielonego fragmentu pamięci gdzieś w stercie. Jeśli często będziesz wywoływał funkcję gettext, pamięć ta będzie zachowywana "aktualna", a nie zamieniana. Jeśli nie zadzwonisz na numer gettext przez jakiś czas, może zostać zamieniony. Ale odnosi się to do "żadnego z ciągów przechowywanych w pliku wykonywalnym nie użyto ostatnio, więc zostały zamienione".

3) Myślę, że angielski (lub "bez wybranego języka") traktowany jest dokładnie tak samo, jak każdy inny wariant językowy.

będę miał trochę dalej kopać trochę, trzeba pierwsze śniadanie ...

bardzo nienaukowe:

#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); 
} 

daje następujące wyniki:

$ 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 

w kodzie dcigettext.c używa mieszaniny wyszukiwania binarnego w płaskiej tablicy łańcuchów i funkcji mieszania, która miesza łańcuch z hashami PJW (patrz: http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html).

Tak więc, narzut, po uruchomieniu aplikacji, wydaje się być "zauważalny" (przy zliczaniu cykli zegara), ale niezbyt duży.

Dokładny czas potrzebny na uruchomienie pierwszego sprintf jest nieco inny w obu przypadkach, więc nie powiedziałbym, że "używanie gettext" przyspiesza sprintf przy pierwszym połączeniu - tylko "pech" w tym biegu (I miał kilka innych wariantów kodu, a wszystkie one różnią się znacznie w pierwszym wywołaniu sprintf, a mniej w przypadku późniejszych połączeń). Prawdopodobnie niektóre ustawienia (prawdopodobnie pamięci podręczne [printf powodujące zastąpienie buforów innymi śmieciami] jest całkiem prawdopodobne], prognozy rozgałęzień itp.) Gdzieś, co zajmuje więcej czasu ...

To oczywiście nie odpowiada na pytania dotyczące stronicowania itp. I nie próbowałem zrobić szwedzkiego, portugalskiego ani niemieckiego tłumaczenia mojego komunikatu "Hello, World". Nadal uważam, że nie jest on ogromny, chyba że faktycznie wykonujesz setki instancji aplikacji na sekundę, a ta aplikacja nie robi nic innego niż wydrukowanie wiadomości na ekranie po wykonaniu prostych obliczeń, na pewno może to być ważne .

Jedynym PRAWDZIWYM sposobem, aby dowiedzieć się, jak dużą różnicę stanowi kompilacja tego samego zastosowania z #define _(x) x zamiast #define _(x) gettext(x) i zobacz, czy zauważysz jakąkolwiek różnicę.

Nadal uważam, że "stronicowany" to czerwony śledzia. Jeśli urządzenie znajduje się pod WYSOKIM nadciśnieniem, to będzie ono działało wolno bez względu na to, co (jeśli napiszę kod, który przydziela 16 GB [mam 16 GB pamięci RAM w komputerze] na moim komputerze, prawie wszystko oprócz samej klawiatury (może migać diodą num-lock), a sam wskaźnik myszy (może przesuwać wskaźnik myszy po ekranie) przestaje reagować).

+0

Dzięki za odpowiedź; Tablica haszująca oznaczałaby AFAIK, że w przypadku każdego tłumaczenia hasz musiałby zostać utworzony w czasie wykonywania, a hasz musi znaleźć się w tabeli. W takim przypadku Twoja aplikacja "Hello World" prawdopodobnie będzie działać kilka razy wolniej niż bez gettext. – Robby75

+0

co do punktu 2: Jest bardzo ważne, gdzie się znajdują. Kiedy znajdują się w pobliżu głównego programu, szansa jest bardzo duża, że ​​obie znajdują się na tej samej stronie! Program rozłożony na wiele stron spowoduje wiele więcej błędów na stronie niż program korzystający tylko z jednej strony. – Robby75

+0

Tak, ale tekst znajdujący się na tej samej stronie, co kod, wymaga, aby kod był bardzo mały (mniej niż 4 KB). Jeśli mówimy o kodzie, który faktycznie robi coś sensownego i pożytecznego, poza drukowaniem "Hello, World \ n", prawdopodobnie kod i tekst obejmują co najmniej jedną stronę. Obliczanie skrótu dla ciągu znaków nie jest całkowicie banalne, ale prostsze niż przetwarzanie, które printf robi na ciąg formatu, więc nie wierzę, że właśnie tu jesteś. Ale jadłem śniadanie, teraz patrząc na to, co faktycznie robi libintl. –

1

Niektóre pomiary:

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

n = 10000000 (10 milionów), a wyjście przekierowanie do pliku. Brak pliku po dla ustawień narodowych, więc oryginalny ciąg jest drukowany (identyczny plik wyjściowy). Użytkownik Czas w sekundach:

  • 0,23 z I18N nieokreślonej
  • 4.43 z I18N
  • 2,33 z I18N i LC_ALL = C

Overhead 0,4 mikrosekundy na wezwanie. (Na Phenom X6 @ 3.6GHz, Fedora 19). Przy LC_ALL = C narzut wynosi tylko 0,2 μs. Zauważ, że jest to prawdopodobnie najgorszy przypadek - zwykle robisz coś więcej w swoim programie. Mimo to jest to czynnik 20, który obejmuje IO. gettext() jest wolniejszy, niż się spodziewałam.

Używanie pamięci Nie zmierzyłem, ponieważ prawdopodobnie zależy to od rozmiaru pliku po. Czas rozpoczęcia Nie mam pojęcia, jak zmierzyć.

+0

Może ustawić n na 1 i uruchomić go w skrypcie: 'for i in \ 'seq 10000000 \'; wykonaj run_test_program; done' do pomiaru czasu startowego. W rzeczywistości ten scenariusz jest dokładnie tym, co robi się w realnym świecie bardzo często za pomocą narzędzi wiersza polecenia. – Robby75

+0

Inna sprawa: Mówisz, że nie masz pliku po, co oznaczałoby, że w przypadku pliku po obciążenie byłoby jeszcze większe, ponieważ musiałoby analizować/wyszukiwać/etc - tak więc najgorszy najgorszy scenariusz; -) – Robby75