Вопрос свой адресую, даже не к знатокам библиотеки
Blitz++, и даже не к
знатокам С++, а скорее к специалистам по компиляторам, а конкретно по
g++.
Понадобился мне недавно контейнер для работы с одно и двумерными массивами
нулей и единиц, а если проще то с битовым потоком одной системы связи.
Требования были примерно такие:
1. легкость инициализации контейнера данными;
2. поддержка срезов(slice);
3. поддержка видов(view);
4. низкие накладные расходы на адресацию;
5. приветствовались всякие навороты, вроде поддержки раворачивания выражений и т.п.
Естественно выбор пал на
Blitz++, который удовлетворял, казалось (в соответствии
с документацией и интернет публикациями), всем требованиям. И все действительно
было здорово, пока на нем не была реализована одна из частей проекта (декодер
Витерби, если кому интересно). А точнее, пока этот декодер небыл протестирован на
производительность под двумя ОС,
Linux и
Windows, с компиляторами
g++ 4.3.3 и
Visual C++ 2008 соответственно. Версия собранная под Linux показала более чем
шести кратный поригрыш, по сравнению с тем же самым кодом собранным под Windows
(естественно на том же самом компьютере). В обоих случаях использовались
одинаковые режимы оптимизации (проверялись
-Os,
-O2 и
-O3 )
WTF !!!
Начали разбираться. Выяснили, что
g++ отказывается оптимизировать перегруженный
в
Blitz++ для класса
Array оператор доступа по индексу, а именно
operator(). Тесты
показали что он по производительности уступает даже
std::list, не говоря уже о
std::vector,
std::valarray и тем более сишным массивам. Оператор доступа по индексу
для blitz::Array уступал в производительности своему собрату из
std::vector почти
в 6 раз.
Далее провели небольшое исследование по сравнению кода генерируемого компилятором
Visual C++ 2008 и
g++ 4.3.3, для операторов доступа к элементам контейнера по
индексу для контейнеров
blitz::Array и
std::vector. И вот что получилось.
Код для blitz::Array:
Array <int, 1> vec(size);
for(int cnt_num=0; cnt_num < cnt; ++cnt_num)
{
for(int i=0; i<size; ++i)
vec(i) = i;
}
Код для std::vector:
vector<int> vec(size);
for(int cnt_num=0; cnt_num < cnt; ++cnt_num)
{
for(int i=0; i<size; ++i)
vec[i] = i;
}
WIN32 — MSVC 2008
------------ BLITZ ----------------
CPU Disasm
Address Hex dump Command
004010AF |. 90 NOP
004010B0 |> 33C0 /XOR EAX,EAX
004010B2 |. 8BCF |MOV ECX,EDI
004010B4 |> 8901 |/MOV DWORD PTR DS:[ECX],EAX
004010B6 |. 40 ||INC EAX
004010B7 |. 03CA ||ADD ECX,EDX
004010B9 |. 3D D8000000 ||CMP EAX,0D8
004010BE |.^ 7C F4 |\JL SHORT 004010B4
004010C0 |. 83EE 01 |SUB ESI,1
004010C3 |.^ 75 EB \JNE SHORT 004010B0
------------ VECTOR ---------------
CPU Disasm
Address Hex dump Command
00401070 |> /33C0 /XOR EAX,EAX
00401072 |> |890481 |/MOV DWORD PTR DS:[EAX*4+ECX],EAX
00401075 |. |40 ||INC EAX
00401076 |. |3D D8000000 ||CMP EAX,0D8
0040107B |. ^|7C F5 |\JL SHORT 00401072
0040107D |. |83EA 01 |SUB EDX,1
00401080 |.^ \75 EE \JNE SHORT 00401070
LINUX — GCC 4.3.3
------------- BLITZ ----------------
8048a40: 8b 45 e8 mov -0x18(%ebp),%eax
8048a43: 31 d2 xor %edx,%edx
8048a45: eb 19 jmp 8048a60 <main+0xf0>
8048a47: 89 f6 mov %esi,%esi
8048a49: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
8048a50: 8b 45 e8 mov -0x18(%ebp),%eax
8048a53: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8048a59: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
8048a60: 0f af c2 imul %edx,%eax
8048a63: 89 14 81 mov %edx,(%ecx,%eax,4)
8048a66: 83 c2 01 add $0x1,%edx
8048a69: 81 fa d8 00 00 00 cmp $0xd8,%edx
8048a6f: 75 df jne 8048a50 <main+0xe0>
8048a71: 83 c6 01 add $0x1,%esi
8048a74: 81 fe 80 96 98 00 cmp $0x989680,%esi
8048a7a: 75 c4 jne 8048a40 <main+0xd0>
------------------ VECTOR -----------------
8048578: 31 d2 xor %edx,%edx
804857a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8048580: 89 14 90 mov %edx,(%eax,%edx,4)
8048583: 83 c2 01 add $0x1,%edx
8048586: 81 fa d8 00 00 00 cmp $0xd8,%edx
804858c: 75 f2 jne 8048580 <main+0x40>
804858e: 83 c1 01 add $0x1,%ecx
8048591: 81 f9 80 96 98 00 cmp $0x989680,%ecx
8048597: 75 df jne 8048578 <main+0x38>
Как видно из приведенного выше, оптимизатор от студии великолепно справился со
своей задачей и создал почти идентичный код для обоих контейнеров. Чего нельзя
сказать о
G++, код для
std::vector у него получился вполне приличный, а вот с
blitz::Array у него вышла какая-то не реальная хрень. Я не большой знаток
AT&T-шного синтаксиса, но помоему творится там какая-то муть.
Посему, хотелось бы услышать мнение общественности.
З.Ы. Форматирование асемблерного дампа так и не осилил, но очень-очень старался, так что звиняйте