Я начинаю использовать VTune. В качестве примера для обучения, я пытаюсь использовать свою микро-оптимизацию в режиме отладки. Вот пример игрушек из моей кодовой базы. Этот код появляется в C++ не- const
метода, и «.data_length» является int
поля объекта (смещение 32 байта), как правило, большое количества:Что означает этот профиль оптимизации цикла?
for (int i=0;i<data_length;++i) { /*...*/ }
VTune услужливо показал мне сборку (от MSVC 2013) для цикла for. Обратите внимание на номера производительности в секундах (я удалил все тайминги, которые не регистрируются). Я также добавил аннотацию: (? Почему не регистр, геэз)
0x140433084 mov dword ptr [rsp+0x588], 0x0 | | ;"i=0"
0x14043308f jmp 0x1404330a1 <Block 77> | | ;jump to compare and loop body
| |
0x140433091 Block 76: | | ;"++i"
0x140433091 mov eax, dword ptr [rsp+0x588] | 0.451 |
0x140433098 inc eax | 0.002 |
0x14043309a mov dword ptr [rsp+0x588], eax | |
| |
0x1404330a1 Block 77: | | ;if (!(i<data_length)) goto next section
0x1404330a1 mov rax, qword ptr [rsp+0x6f0] | 0.407 |
0x1404330a9 mov eax, dword ptr [rax+0x20] | | ; move "data_length" into "eax".
0x1404330ac cmp dword ptr [rsp+0x588], eax | 1.195 | ; "i<data_length;"
0x1404330b3 jnl 0x140433106 <Block 80> | |
0x1404330b5 Block 78: | |
. . . | | ;Loop body. There's a jmp in here to
| | ; block 76.
| |
0x140433106 Block 80: | | ;code following loop
Что это говорит мне о том, что загрузка i
, чтобы увеличить его навлечь неудачу кэширования. Во-вторых, тестовая логика довольно вялая - особенно загружая «.data_length» каждый раз.
Я понял, почему бы не загрузить его один раз, а затем использовать декремент:
for (int i=data_length-1;i>=0;--i) { /*...*/ }
Сборку и сроки выглядеть:
0x140433084 mov rax, qword ptr [rsp+0x6f0] | | ;Same code, but now only happens once!
0x14043308c mov eax, dword ptr [rax+0x20] | |
0x14043308f dec eax | | ;"data_length-1"
0x140433091 mov dword ptr [rsp+0x588], eax | | ;"i=data_length-1;"
0x140433098 jmp 0x1404330aa <Block 77> | | ;jump to compare and loop body
| |
0x14043309a Block 76: | | ;"++i"
0x14043309a mov eax, dword ptr [rsp+0x588] | 0.357 |
0x1404330a1 dec eax | 0.002 |
0x1404330a3 mov dword ptr [rsp+0x588], eax | |
| |
0x1404330aa Block 77: | | ;if (i<0) goto next section
0x1404330aa cmp dword ptr [rsp+0x588], 0x0 | 0.401 | ; "i>=0;"
0x1404330b2 jl 0x140433105 <Block 80> | 2.806 |
0x1404330b4 Block 78: | |
. . . | | ;Loop body. Same as above, I think.
| |
0x140433105 Block 80: | | ;code following loop
Посмотрите на это jl
! Три секунды для прыжка? Я подумал, что, возможно, это место не было в кэше команд, но, как вы видите, на самом деле это довольно близко (сразу после тела цикла, как и следовало ожидать). Что еще более важно, первый метод должен иметь такую же проблему. Первая версия jnl
даже не зарегистрировалась.
Я предполагаю, что его время съедается в теле цикла, хотя это странно, что это происходит в одном случае, но не в другом. У меня есть больше работы, чтобы посмотреть на это?
Я написал все это, и теперь, снова глядя, я думаю, что это может быть скучная проблема с предсказанием ветвей. Процессоры любят брать ветви назад в циклы, но в этом случае ветвь к блоку 80 не должна приниматься в большинстве случаев в течение.
Я определенно все еще учусь этому, так предполагая, я аннотированный все в основном правильно, у меня есть несколько вопросов:
- я прав, полагая, что
i
следует, вероятно, регистр, и он станет одним в оптимизированном режиме? - Что бы ни случилось с этим
jl
во второй версии? Действительно ли предсказание ветвления терпит неудачу? Почему он не появляется в следующей инструкции?
Edit: CPU это испытывается на это Intel 990X (Gulftown, 2011).
Пытаться оптимизировать сгенерированный код в режиме отладки - пустая трата времени. Вам нужно использовать режим выпуска. –
@RichardCritten Я знаю. Вот почему это «игрушечный пример» для «образовательных» целей. Надо идти, прежде чем бежать. – imallett