
Возможно не только мне интересно, а каков микрокод инструкции XCHG на RISC для x86 CISC?Например ни для кого не секрет, что на языках высокого уровня, чтобы обменять значениями две переменные «X» и «Y», нужна ещё одна переменная, скажем «Z».
X=5, Y=7
Z=Y
Y=X
X=Z
X=7, Y=5
Но, процессоры это умеют делать командой XCHG, причём, явно никакой третьей переменной здесь вроде бы как и нет…
X=5, Y=7
XCHG X, Y
X=7, Y=5
Я даже предполагал что сама аббревиатура «XCHG», это ни что иное как «XOR CHANGE», сразу скажу что подтверждения этой догадки я нигде не встречал. Почему XOR CHANGE? Возможно потому что обмен между регистрами происходит с участием логической команды XOR.
X=5, Y=7
XOR X, Y
XOR Y, X
XOR X, Y
X=7, Y=5
Что ж, я решил проверить свою теорию, промерив продолжительность исполнения инструкции «XCHG» и её аналога «XOR, XOR, XOR». Ну а чтобы результаты были максимально детерминированными, я решил запустить всё это дело ещё до загрузки какой-либо операционной системы, т.е. сразу после того, как БИОС компьютера решит загружаться с определённого накопителя. В общем для максимальной чистоты эксперимента, я разместил приведённый ниже код прямо в MBR загрузочного диска (в своём случае я использовал флешку).
Следующий код повторяет инструкцию «XCHG EDI, EAX» 7 раз, а инструкцию «XOR» — 21 раз ну и накапливает затраченные тики процессора. Цикл для каждой тестируемой команды повторяется по 10000 раз. После чего всё это прокручивается ещё и ещё (всего 20 раз), в итоге вычисляется среднее. Как по мне, тест получается довольно «чистый», более-менее детерминированный. Ну а что касается того, равны ли по продолжительности исполнения команда XCHG и три команды XOR, то судя по этому тесту, XCHG выполняется на 5% быстрее, что никак не вписывается в мою теорию 🙂
до этого момента инициализация сегментных регистров + возможные перемещения блоков кода mov ax, 3 int 10h cli ; запретим прерывания mov al, 0FFh out 021h, al out 0A1h, al mov cx, 20 ; сделаем 20 попыток again: push cx xor ebp, ebp mov si, 10000 ; повторим 10000 раз для XCHG @@: xor eax, eax xor edi, edi cpuid ; заставим ЦПУ выполнить все предыдущие команды rdtsc rept 7 { xchg edi, eax } ; повторим 7 раз XCHG cpuid rdtsc sub eax, edi ; вычтем разницу add ebp, eax ; суммируем результаты dec si jnz @B mov [_xchg], ebp xor ebp, ebp mov si, 10000 ; повторим 10000 раз для XOR @@: xor eax, eax xor edi, edi cpuid rdtsc rept 7 { xor edi, eax ; повторим 7 раз по три XOR xor eax, edi xor edi, eax } cpuid rdtsc sub eax, edi add ebp, eax dec si jnz @B next: mov [_xor], ebp mov eax, [_xchg] add [totalxchg], eax mov di, [screen] call print add word [screen], 32 mov eax, [_xor] add [totalxor], eax mov di, [screen] call print add word [screen], 128 pop cx dec cx jnz again dec byte [color+1] mov eax, [totalxchg] mov ebx, 20 xor edx, edx idiv ebx mov di,[screen] call print mov eax, [totalxor] mov ebx, 20 xor edx, edx idiv ebx add word [screen], 32 mov di,[screen] call print @@: jmp @B ; на этом всё. print: mov ebx, 10 ; подпрограмма вывода полученных значений на экран xor cx, cx more: mov si, bufferdec+12 xor edx, edx sub si, cx idiv ebx add dl, '0' mov [si], dl inc cx test cl, 1 je @F test cl, 2 je @F or eax, eax je @F mov [si-1], byte '.' inc cx @@: or eax, eax jne more color: mov ah, 7 push 0b800h pop es mov si, bufferdec+12 add di, cx add di, cx std @@: lodsb stosw loop @b push cs pop es ret screen: dw 0 _xchg: dd 0 totalxchg: dd 0 _xor: dd 0 totalxor: dd 0 bufferdec: db 12 dup 0 rb 510 - ($ - $$) db 55h,0AAh
ссылка на оригинал статьи https://habr.com/ru/post/560690/
Добавить комментарий