2016-04-24 2 views
1

В настоящее время я выполняю назначение, которое измеряет производительность различных команд x86-64 (в формате t).x86-64 Относительная производительность jmp

Команда, с которой я немного смущен, является командой «безусловный jmp». Вот как я его реализовал:

.global uncond 
uncond: 

.rept 10000 
jmp . + 2 
.endr 


mov $10000, %rax 
ret 

Это довольно просто. Код создает функцию, называемую «uncond», которая использует директиву .rept для вызова команды jmp 10000 раз, затем устанавливает возвращаемое значение на количество раз, когда вы вызывали команду jmp.

"." in at & t синтаксис означает текущий адрес, который я увеличиваю на 2 байта, чтобы учесть собственно инструкцию jmp (поэтому jmp. + 2 должен просто перейти к следующей команде).

Код, который я не показал, вычисляет количество циклов, необходимых для обработки команд 10000.

Мои результаты говорят, что jmp довольно медленный (занимает 10 циклов для обработки одной команды jmp), но из того, что я понимаю о конвейерной обработке, безусловные переходы должны быть очень быстрыми (ошибки предсказания ветвления).

Я что-то упустил? Является ли мой код неправильным?

+0

Возможный дубликат [Медленная jmp-инструкция] (http://stackoverflow.com/questions/38811901/slow-jmp-instruction).Этот более подробный вопрос имеет намного лучший и более подробный ответ. –

ответ

1

Процессор не оптимизирован для не-оп jmp инструкции, поэтому он не обрабатывает особый случай продолжения декодирования и инструкции JMP трубопроводов, которые просто перейти к следующему insn.

ЦП оптимизированы для петель. jmp . будет работать на одном входе в часы на многих процессорах или один на 2 такта на некоторых процессорах.


Скачок создает пузырь в наборе команд. Один хорошо предсказанный прыжок в порядке, но ничего не работает, но прыжки проблематичны. Я воспроизвел свои результаты на Core2 E6600 (Merom/Conroe microarch):

# jmp-test.S 
.globl _start 
_start: 

    mov $100000, %ecx 
jmp_test: 
    .rept 10000 
    jmp . + 2 
    .endr 

    dec %ecx 
    jg jmp_test 


    mov $231, %eax 
    xor %ebx,%ebx 
    syscall   # exit_group(0) 

строить и работать с:

gcc -static -nostartfiles jmp-test.S 
perf stat -e task-clock,cycles,instructions,branches,branch-misses ./a.out 

Performance counter stats for './a.out': 

     3318.616490  task-clock (msec)   # 0.997 CPUs utilized   
    7,940,389,811  cycles     # 2.393 GHz      (49.94%) 
    1,012,387,163  instructions    # 0.13 insns per cycle   (74.95%) 
    1,001,156,075  branches     # 301.679 M/sec     (75.06%) 
      151,609  branch-misses    # 0.02% of all branches   (75.08%) 

     3.329916991 seconds time elapsed 

С другой перспективе:

7,886,461,952  L1-icache-loads   # 2377.687 M/sec     (74.95%) 
    7,715,854  L1-icache-load-misses  # 2.326 M/sec     (50.08%) 
1,012,038,376  iTLB-loads    # 305.119 M/sec     (75.06%) 
      240  iTLB-load-misses   # 0.00% of all iTLB cache hits (75.02%) 

(Числа в (%) в конце каждой строки, сколько из общего времени работы счетчика было активным: perf должен мультиплексировать для вас, когда вы просите его подсчитать больше вещей, чем HW может засчитываться сразу).

Так что на самом деле это не промахи, связанные с I-cache, это просто узкие места для извлечения/декодирования команд, вызванные постоянными прыжками.

My SnB машина сломана, поэтому я не могу проверить цифры на ней, но 8 циклов на устойчивую пропускную способность jmp довольно близки к вашим результатам (вероятно, из другой микроархитектуры).

Для получения дополнительной информации см. http://agner.org/optimize/ и других ссылках от тег wiki.

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