Ответ 1
Идентичная (и довольно хорошая) производительность при использовании LANG = C против LANG = ожидается что-либо еще для реализации glibc, используемой Linux.
Ваши результаты в Linux имеют смысл. Ваш метод тестирования, вероятно, одобрен. Используйте профилировщик, чтобы узнать, сколько времени ваш микрозапуск стоит внутри функций Windows. Если проблема с реализацией Windows оказывается проблемой, может быть, есть функция Windows, которая может конвертировать целые строки, например С++ boost::to_upper_copy<std::string>
(если это еще медленнее, см. ниже).
Также обратите внимание, что восходящая строка ASCII может быть очень проста в векторе SIMD. Я написал функцию флип-флага для одного вектора в другом ответе, используя C SSE intrinsics; он может быть адаптирован к верхнему регистру вместо флипса. Это должно быть огромным ускорением, если вы потратите много времени на то, чтобы строки, длина которых больше 16 байтов, и что вы знаете, это ASCII.
На самом деле, Boost to_upper_copy(), похоже, компилируется в чрезвычайно медленный код, например, в 10 раз медленнее, чем toupper
. См. Эту ссылку для моего векторизованного strtoupper(dst,src)
, который является только ASCII, но может быть расширен с помощью резервного копирования, когда обнаружены байты без ASCII src.
Как ваш текущий код обрабатывает UTF-8? Там не так много выигрыша в поддержке не-ASCII-локалей, если вы предполагаете, что все символы являются одним байтом. IIRC, Windows использует UTF-16 для большинства вещей, что является неудачным, потому что оказалось, что мир хочет более 2 ^ 16 кодовых точек. UTF-16 представляет собой кодировку Unicode с переменной длиной, например UTF-8, но без преимущества чтения ASCII. Фиксированная ширина имеет много преимуществ, но, к сожалению, вы не можете предположить, что даже с UTF-16. Java тоже допустил эту ошибку и застрял в UTF-16.
#define __ctype_toupper \
((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)
int toupper (int c) {
return c >= -128 && c < 256 ? __ctype_toupper[c] : c;
}
Asm из x86-64 Ubuntu 15.10 /lib/x86_64-linux-gnu/libc.so.6
:
## disassembly from objconv -fyasm -v2 /lib/x86_64-linux-gnu/libc.so.6 /dev/stdout 2>&1
toupper:
lea edx, [rdi+80H] ; 0002E300 _ 8D. 97, 00000080
movsxd rax, edi ; 0002E306 _ 48: 63. C7
cmp edx, 383 ; 0002E309 _ 81. FA, 0000017F
ja ?_01766 ; 0002E30F _ 77, 19
mov rdx, qword [rel ?_37923] ; 0002E311 _ 48: 8B. 15, 00395AA8(rel)
sub rax, -128 ; 0002E318 _ 48: 83. E8, 80
mov rdx, qword [fs:rdx] ; 0002E31C _ 64 48: 8B. 12
mov rdx, qword [rdx] ; 0002E320 _ 48: 8B. 12
mov rdx, qword [rdx+48H] ; 0002E323 _ 48: 8B. 52, 48
mov eax, dword [rdx+rax*4] ; 0002E327 _ 8B. 04 82 ## the final table lookup, indexing an array of 4B ints
?_01766:
rep ret ; actual objconv output shows the prefix on a separate line
Итак, для начала требуется, чтобы аргумент arg не находился в диапазоне 0 - 0xFF (поэтому эта ветка должна быть предсказуема совершенно не принятой), в противном случае она найдет таблицу для текущей локали, которая включает в себя три вида разметки указателя: одна загрузка из глобальной и одна нить-локальная, и еще одно разыменование. Затем он фактически индексируется в таблицу с 256 входами.
Это вся библиотечная функция; метка toupper
в разборке - это то, что вызывает ваш код. (Ну, через слой косвенности через PLT из-за динамической компоновки, но после того, как первый вызов вызывает ленивый поиск символов, это всего лишь одна дополнительная инструкция jmp
между вашим кодом и теми 11 insns в библиотеке.)