Blitz++, G++ и operator()
От: first_slider  
Дата: 11.04.09 19:59
Оценка:
Вопрос свой адресую, даже не к знатокам библиотеки 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-шного синтаксиса, но помоему творится там какая-то муть.

Посему, хотелось бы услышать мнение общественности.

З.Ы. Форматирование асемблерного дампа так и не осилил, но очень-очень старался, так что звиняйте
blitz++ g++ opimization оптимизация
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.