2013-08-16 66 views
5

我剛剛通過了http://www.gnu.org/software/gettext/manual/gettext.html的文檔,沒有任何關於性能開銷的討論。在互聯網上,我發現只有其他語言(PHP和Java)的性能討論,但沒有任何C/C++。C/C++中gettext國際化系統的性能開銷

所以我的問題:??

  1. 什麼是程序的啓動過程中使用的gettext(負載共享庫如何翻譯加載到內存的性能開銷在啓動時加載或導通所有的翻譯需求?)

  2. 程序正常運行時的性能損失是多少? (即需要翻譯時)程序的內存佔用量增加了多少?內存的組織方式如何?當程序閒置時,程序的某些部分是否交換到磁盤有更高的危險/可能性? (如果翻譯與程序的其餘部分存儲在非常不同的部分內存中,那麼根據我的理解,與非國際化版本的程序相比,頁面錯誤的機會更高)

  3. 在「C」-locale下運行的程序是否也會受到這些性能處罰?

非常感謝。

+0

@JamesKanze該程序可能會產生較長的報告,或者它可能會發送個性化的羣發電子郵件,或...輸出是人類可讀的意味着周圍有一個人,並且該程序可以停止,直到他/她讀完輸出爲止。 – Chris

+0

@James:即使只有一個導致頁面錯誤的單個丟失字節也會導致延遲,即使是由人類引起的延遲也是如此。在極端情況下,如果硬盤必須旋轉,可能會延遲數秒。也有相當多的命令行程序是從腳本啓動的,這可能意味着程序幾千次啓動/停止。但真正的一點是,「這是可以忽略的」並不是對這些問題的回答,因爲在某些情況下它只是可忽略不計的。 – Robby75

回答

3

鑑於替代這樣的做法是有大量的構建,每個與它是這樣的:

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

,而不是我們得到:

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

這是很容易閱讀和理解。

我不知道gettext實現的確切結構,但我希望它是一個哈希表,一旦它被加載。可能是二叉樹,但散列表似乎更明智。

至於確切的開銷,很難在其上放置一個數字 - 特別是,正如您所說,如果將某些內容交換到磁盤,並且磁盤已停止,則需要3-4秒才能啓動磁盤加速。那麼你如何量化?是的,如果系統一直在忙着做一些內存密集的事情,那麼gettext所需的頁面可能會被換出。

如果文件非常大,加載消息文件應該只是一個很大的開銷,但是如果磁盤沒有旋轉,並且文件沒有被緩存,那麼會有幾秒的開銷。再次,如何量化。文件的大小顯然與翻譯(或母語)消息的實際大小成正比。

關於第2點:

據我所知,在Linux和Windows,頁面上的「最近最少使用」(或其他使用情況的統計)的基礎上,它有無關換出他們所在的位置。顯然,翻譯後的消息與實際代碼不同 - 在源文件中沒有15個不同翻譯的列表,所以翻譯在運行時加載,並且位於與代碼本身不同的地方。然而,這樣做的開銷是類似的開銷差別:考慮到文本字符串在程序的二進制通常放在一起,反正

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

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

,我不知道認爲它們對執行代碼的「接近度」與堆中某處​​動態分配的內存顯着不同。如果經常調用gettext函數,那麼內存將保持「最新」並且不會換出。如果您一段時間沒有撥打gettext,它可能會被換出。但是,這適用於「最近沒有使用可執行文件存儲的字符串,所以它們被換出」。

3)我認爲英語(或「沒有語言選擇」)被視爲完全相同的任何其他語言變體。

我將有一個有點遠一點挖,需要早餐第一...

非常不科學的:

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

得出以下結果:

$ 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 

中的代碼dcigettext.c在平面字符串數組中使用二進制搜索的混合形式,以及將字符串哈希到PJW哈希的哈希函數(請參閱:http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html)。

因此,一旦應用程序啓動,開銷似乎在「明顯」(當計算時鐘週期時),但不是很大。

運行第一個sprintf所需的確切時間在兩種情況下都有所不同,所以我不會說在第一次調用時使用gettext會使sprintf更快 - 只是「運氣不好」(I有一些其他的代碼變體,並且它們在第一次調用sprintf時差異很大,稍後調用時變化較小。可能有些設置(可能緩存[printf導致緩存被其他垃圾覆蓋很可能],分支預測等),需要額外的時間...

現在,這顯然不能回答您關於尋呼的問題等等。而且我沒有嘗試對我的「Hello,World」消息做一個瑞典語,葡萄牙語或德語翻譯。我仍然相信這並不是很大,除非你確實每秒鐘運行100個應用程序的實例,並且該應用程序除了在進行一些簡單的計算之後將消息打印到屏幕上之外沒有其他更多的工作,當然,這可能是重要的。

唯一真正的方法是找出它與#define _(x) x而不是#define _(x) gettext(x)相同的應用程序,看看你是否有任何區別。

我仍然認爲「翻頁」是一個紅色的鯡魚。如果機器處於高內存壓力下,那麼無論如何,它都會運行緩慢(如果我在機器上寫了一段代碼分配16GB [我機器上有16GB內存],除了鍵盤本身外幾乎所有內容(可以閃爍數字鎖定LED)並且鼠標指針本身(可以在屏幕上移動鼠標指針)無響應)。

+0

感謝您的回覆;哈希表表示AFAIK,對於每個翻譯,哈希將不得不在運行時創建,並且哈希必須在表中找到。在這種情況下,您的「Hello World」應用程序可能會比不使用gettext慢幾倍。關於第2點的 – Robby75

+0

:它們位於何處非常重要。當它們位於主程序附近時,兩者都處於同一頁面的機會非常高!一個分佈在很多頁面上的程序會導致比僅使用一個頁面的程序更多的頁面缺失。 – Robby75

+0

是的,但要使文本與代碼要求代碼非常小(小於4KB)在同一頁上。如果我們談論的是一些實際上做了有意義且有用的代碼,除了印刷「Hello,World \ n」之外,它的代碼和文本很可能至少涵蓋單頁。 計算一個字符串的散列並不是很平凡,但比printf對格式字符串的處理更簡單,所以我不相信你就在那裏。但是我已經吃過早飯了,現在看看libintl實際上做了什麼。 –

1

有些測量:

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

在N = 10000000(10元),並輸出重定向到文件中。沒有語言環境的po文件,所以打印原始字符串(相同的輸出文件)。用戶時間(秒):

  • 0.23與I18N未定義
  • 4.43與I18N
  • 2.33與I18N和LC_ALL = C

的每個呼叫0.4微秒的開銷。 (在Phenom X6 @ 3.6GHz,Fedora 19上)。 LC_ALL = C時,開銷僅爲0.2μs。請注意,這可能是最糟糕的情況 - 通常你會在你的程序中做更多的事情。儘管如此,它還是20倍,包括IO。 gettext()比我預期的要慢。

內存使用我還沒有測量,因爲它可能取決於po文件的大小。啓動時間我不知道如何測量。

+0

也許將n設置爲1並在腳本中啓動它:'for i in \'seq 10000000 \';做run_test_program;完成'來衡量啓動時間。事實上,這種情況正是現實世界中常常使用命令行工具所做的事情。 – Robby75

+0

另一件事:你說你沒有po文件,這意味着用po文件的開銷仍然會更大,因爲它將不得不分析/搜索/等 - 所以一個更糟糕的最糟糕的情況; - ) – Robby75