6

我刚刚浏览了http://www.gnu.org/software/gettext/manual/gettext.html的文档,根本没有讨论性能开销。在 Internet 上,我只找到了其他语言(PHP 和 Java)的性能讨论,但没有找到 C/C++。

因此我的问题:

  1. 使用 gettext 的程序启动期间的性能开销是多少(加载共享库?翻译如何加载到内存中?所有翻译都是在启动时加载还是按需加载?)

  2. 程序正常运行期间的性能损失是多少?(即需要翻译时)程序增加的内存占用有多大,内存是如何组织的?当程序空闲时,程序的某些部分被交换到磁盘是否存在更高的危险/可能性?(如果翻译存储在与程序的其余部分完全不同的内存部分中,那么据我了解,页面错误的可能性高于与程序的非国际化版本相比)

  3. 在“C”语言环境下运行的程序是否也会遭受这些性能损失?

非常感谢。

4

2 回答 2

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 =10123
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 更快 - 只是在这次运行中“运气不好”(我有一些其他变体的代码,它们在第一次调用时变化很大sprintf,而在以后的调用中变化不大)。可能某些设置(可能缓存 [printf 导致缓存很可能被其他垃圾覆盖]、分支预测等)需要额外的时间......

现在,这显然不能回答您关于分页等问题。而且我没有尝试对我的“Hello, World”消息进行瑞典语、葡萄牙语或德语翻译。我仍然相信它不是很大,除非您确实每秒运行 100 次应用程序的实例化,并且该应用程序除了在进行一些简单的计算后在屏幕上打印一条消息之外并没有做太多事情,当然,这可能很重要.

找出它有多大差异的唯一真正方法是使用#define _(x) x而不是编译相同的应用程序#define _(x) gettext(x),看看您是否注意到任何差异。

我仍然认为“分页”是一个红鲱鱼。如果机器处于高内存压力下,那么无论如何它都会运行缓慢(如果我在我的机器上编写一段分配 16GB [机器中有 16GB RAM] 的代码,几乎除了键盘本身之外的所有东西(可以闪烁数字锁定 LED)并且鼠标指针本身(可以在屏幕上移动鼠标指针)无响应)。

于 2013-08-16T09:26:50.803 回答
1

一些测量:

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

使用 n = 10000000(1000 万)并将输出重定向到文件。语言环境没有 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 文件的大小。启动时间我不知道如何衡量。

于 2013-08-16T10:08:31.703 回答