Я просто работал с документацией http://www.gnu.org/software/gettext/manual/gettext.html и нет никаких дискуссий о производительности. В Интернете я нашел только обсуждения производительности для других языков (PHP и Java), но ничего для C / C ++.
Поэтому мои вопросы:
Каковы потери производительности при запуске программы, которая использует gettext (загрузить общую библиотеку? Как переводы загружаются в память? Все ли загрузки загружаются при запуске или по запросу?)
Какое снижение производительности при нормальной работе программы? (т. е. когда необходим перевод) Насколько велик объем памяти программы и как она организована? Существует ли более высокая опасность / вероятность того, что части программы будут перенесены на диск, когда программа простаивает? (Если переводы хранятся в другой части памяти, чем остальная часть программы, то, по моему мнению, вероятность сбоя страницы выше, чем по сравнению с не интернационализированной версией программы)
Программа, которая работает под «C» -locale, также переносит эти потери производительности?
Большое спасибо.
Учитывая, что альтернатива этому подходу состоит в том, чтобы иметь большое количество сборок, каждая с чем-то вроде этого:
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» на шведский, португальский или немецкий. Я по-прежнему считаю, что это не так уж и много, если только вы действительно не запускаете сотни экземпляров приложения в секунду, и это приложение ничего не делает, кроме вывода сообщения на экран после выполнения простых вычислений, конечно, это может быть важно ,
Единственный НАСТОЯЩИЙ способ выяснить, насколько это важно, — составить одно и то же приложение с #define _(x) x
вместо #define _(x) gettext(x)
и посмотрите, заметите ли вы разницу.
Я до сих пор думаю, что «вытащил» красная сельдь. Если машина находится под высоким давлением памяти, то она будет работать медленно, несмотря ни на что (Если я напишу фрагмент кода, который выделяет 16 ГБ [у меня 16 ГБ ОЗУ в машине] на моей машине, почти все, кроме самой клавиатуры ( может мигать светодиодом num-lock), а сам указатель мыши (может перемещать указатель мыши по экрану) перестает отвечать на запросы).
Некоторые измерения:
for ( ; n > 0; n--) {
#ifdef I18N
fputs(gettext("Greetings!"), stdout);
#else
fputs("Greetings!", stdout);
#endif
putc('\n', stdout);
}
С n = 10000000 (10 миллионов) и перенаправлением вывода в файл. Для локали нет po-файла, поэтому печатается оригинальная строка (идентичный выходной файл). Время пользователя в секундах:
Накладные расходы 0,4 микросекунды за звонок. (На Phenom X6 @ 3,6 ГГц, Fedora 19). При LC_ALL = C накладные расходы составляют всего 0,2 мкс. Обратите внимание, что это, вероятно, наихудший случай — обычно вы делаете что-то большее в своей программе. Тем не менее, это фактор 20, и это включает в себя IO. gettext () работает медленнее, чем я ожидал.
Использование памяти я не измерял, так как это, вероятно, зависит от размера po-файла. Время запуска я понятия не имею, как измерить.