2015-05-14 4 views
1

Я начинаю использовать 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 не должна приниматься в большинстве случаев в течение.


Я определенно все еще учусь этому, так предполагая, я аннотированный все в основном правильно, у меня есть несколько вопросов:

  1. я прав, полагая, что i следует, вероятно, регистр, и он станет одним в оптимизированном режиме?
  2. Что бы ни случилось с этим jl во второй версии? Действительно ли предсказание ветвления терпит неудачу? Почему он не появляется в следующей инструкции?

Edit: CPU это испытывается на это Intel 990X (Gulftown, 2011).

+2

Пытаться оптимизировать сгенерированный код в режиме отладки - пустая трата времени. Вам нужно использовать режим выпуска. –

+0

@RichardCritten Я знаю. Вот почему это «игрушечный пример» для «образовательных» целей. Надо идти, прежде чем бежать. – imallett

ответ

0

i должен, вероятно, находиться в регистре (например, если оптимизатор компилятора); однако также возможно, что код в теле цикла (не показан) использует все регистры и что для производительности лучше избегать размещения i в регистре (особенно, если тело цикла содержит внутренний цикл или вызов функции или что-то еще).

Я не знаю, что такое ваш процессор (VIA, AMD, Intel, Atom, Xeon, сколько лет), но предсказание ветвей в современных процессорах должно хорошо работать с этой ветвью. Однако, возможно, вы ожидаете ошибочного предсказания ветвления, когда цикл завершается, и если количество итераций невелико (например, data_length-1), одно одиночное предсказание может быть значительным.

+0

Уход. Итак, что заставляет 'jl' быть настолько медленнее, если это неверное предсказание отрасли? Самая маленькая 'data_length-1' может быть равна' 2^10-1'. Обычно это '2^14-1'. См. Edit; это Intel 990X. – imallett

Смежные вопросы