let rec a n x y = match (n, y) with (0, y) -> x+1 | (1, 0) -> x | (2, 0) -> 0 | (3, 0) -> 1 | (n, 0) -> 2 | (n, y) -> (a (n-1) (a n x (y-1)) x) ;; print_int(a 3 8 8); print_newline();;
[html_font color=blue] _Akkr_a_49: sub esp, 8 L106: cmp eax, 7 ja L101 mov edx, eax sar edx, 1 jmp [edx * 4 + L107] .DATA L107 DWORD L105 DWORD L104 DWORD L103 DWORD L102 .CODE L105: add ebx, 2 mov eax, ebx add esp, 8 ret L104: cmp ecx, 1 jne L101 mov eax, ebx add esp, 8 ret L103: cmp ecx, 1 jne L101 mov eax, 1 add esp, 8 ret L102: cmp ecx, 1 jne L101 mov eax, 3 add esp, 8 ret L101: cmp ecx, 1 je L100 mov DWORD PTR 0[esp], eax mov DWORD PTR 4[esp], ebx add ecx, -2 call _Akkr_a_49 L108: mov ebx, eax mov eax, DWORD PTR 0[esp] add eax, -2 mov ecx, DWORD PTR 4[esp] jmp L106 L100: mov eax, 5 add esp, 8 ret
=KRoN=>let rec a n x y = =KRoN=> match (n, y) with =KRoN=> (0, y) -> x+1 =KRoN=> | (1, 0) -> x =KRoN=> | (2, 0) -> 0 =KRoN=> | (3, 0) -> 1 =KRoN=> | (n, 0) -> 2 =KRoN=> | (n, y) -> (a (n-1) (a n x (y-1)) x) =KRoN=> ;; =KRoN=>print_int(a 3 8 8); =KRoN=>print_newline();; =KRoN=>
[html_font color=blue] =KRoN=>_Akkr_a_49: =KRoN=> sub esp, 8 - налицо отсутствие оформления фрэйма стэка - в спорах на этом форуме я не раз упоминал, что в современных языках вызов процедуры одно из самых дорогих удовольствий - фрэйм для локальных переменных, передача параметров через стек и т.д. Здесь произведена резервация под две временные переменные. Надо еще сказать, что параметры передаются через регистры - n - eax, x - ebx, y - ecx. Результат возвращается на eax. =KRoN=>L106: Запомните эту метку!!!! =KRoN=> cmp eax, 7 Все константы и вычисления сдвинуты по формуле 2*n+1 иниыми словами это сравнение cmp eax,3 =KRoN=> ja L101 есла n больше 3, то это общий случай =KRoN=> mov edx, eax иначе преобразуем это в индекс для косвенного перехода - таким образом закодирована первая часть match =KRoN=> sar edx, 1 =KRoN=> jmp [edx * 4 + L107] =KRoN=> .DATA Это собственно таблица косвенной адресации =KRoN=>L107 DWORD L105 n == 0 =KRoN=> DWORD L104 n == 1 =KRoN=> DWORD L103 n == 2 =KRoN=> DWORD L102 n == 3 =KRoN=> .CODE =KRoN=>L105: =KRoN=> add ebx, 2 x + 1 =KRoN=> mov eax, ebx вернуть на eax =KRoN=> add esp, 8 подровнять стек =KRoN=> ret =KRoN=>L104: =KRoN=> cmp ecx, 1 сравнение y == 0 =KRoN=> jne L10 =KRoN=> mov eax, ebx x =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L103: =KRoN=> cmp ecx, 1 y == 0 =KRoN=> jne L101 =KRoN=> mov eax, 1 вернуть 0 =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L102: =KRoN=> cmp ecx, 1 y == 0 =KRoN=> jne L101 =KRoN=> mov eax, 3 вернуть 1 =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L101: общий случай =KRoN=> cmp ecx, 1 y == 0 =KRoN=> je L100 =KRoN=> mov DWORD PTR 0[esp], eax внимание, сохраним n =KRoN=> mov DWORD PTR 4[esp], ebx и х во временные переменные =KRoN=> add ecx, -2 y - 1 =KRoN=> call _Akkr_a_49 вызовем функцию для вычисления a n x ( y - 1 ) - внутренний вызов. =KRoN=>L108: А вот здесь начинается второе место, где объектный верблюд выигрывает. ВНИМАНИЕ. Вместо вызова функции еще раз верблюд подготовил параметры на регистрах и перешел на ту метку, которую я вам советовал завпомнить - сразу после sub esp,8 - тем самым экономия сразу в двух местах - нет операции вызова - она заменена переходом (нет работы со стеком и т.д.) и еще - переиспользова- ние временных переменных без их дополнительного переразмещнния на стеке! =KRoN=> mov ebx, eax результат на eax - поместим его вторым параметром - как x =KRoN=> mov eax, DWORD PTR 0[esp] востановим n =KRoN=> add eax, -2 и отнимем 1 - т.е. мы имеем n - 1 первым параметром =KRoN=> mov ecx, DWORD PTR 4[esp] а бывший x передадим пос- ледним =KRoN=> jmp L106 А вот и тот самый переход вместо вызова!!!!! =KRoN=>L100: =KRoN=> mov eax, 5 здесь возратим 2 =KRoN=> add esp, 8 =KRoN=> ret
С++ Верблюд 2*12 - параметры 0 2*12 - временные переменные 1*8 2*4 - адрес возврата 1*4 - развернули один уровень 2*4 - оформление фрейма 0 2 возврата/вызова один вызов, один переход (двойная операция со стеком) Итого 64 байта на вызов 12 байтов
[html_font color=blue] : AA ( x y n — an ) ?DUP 0= IF DROP 1+ EXIT THEN ( x y n ) SWAP ?DUP IF ( x n y!=0 ) >R 2DUP R> 1- ( x n x n y-1 ) SWAP RECURSE ( x n a ) -ROT 1- RECURSE EXIT THEN ( x n ) DUP 1 = IF DROP ( x ) EXIT THEN DUP 2 = IF 2DROP 0 EXIT THEN 3 = IF 2DROP 1 EXIT THEN DROP 2 ; : A ( n x y — an ) ROT AA ; 3 3 3 A . CR BYE
_TEXT SEGMENT _n$ = 8 смещения до параметров на стеке _x$ = 12 относительно esp _y$ = 16 ?a@@YAHHHH@Z PROC NEAR ; a, COMDAT ; File D:Testsackermanackerman.cpp ; Line 12 push esi фрейм не оформляем - нет локальных переменных ; Line 14 mov esi, DWORD PTR _n$[esp] загрузили n - потеря у верблюда уже на регистре test esi, esi сравнили push edi сохранили регистр - потеря je SHORT $L768 mov ecx, DWORD PTR _y$[esp+4] теперь достаем y&x - потеря mov eax, DWORD PTR _x$[esp+4] $L764: ; Line 17 test ecx, ecx je SHORT $L723 ; Line 18 dec ecx готовимся к внутреннему вызову - и edi еще пихаем - а потом восстанавливаем mov edi, eax push ecx Подготовка параметров push eax push esi call ?a@@YAHHHH@Z ; a add esp, 12 ; 0000000cH выравнивание стека dec esi mov ecx, edi jne SHORT $L764 вот она замена рекурсии!!!! pop edi inc eax pop esi ; Line 27 ret 0 $L768: ; Line 14 mov eax, DWORD PTR _x$[esp+4] pop edi ; Line 15 inc eax pop esi ; Line 27 ret 0 $L723: ; Line 21 dec esi je SHORT $L721 dec esi je SHORT $L729 dec esi je SHORT $L730 pop edi ; Line 26 mov eax, 2 pop esi ; Line 27 ret 0 $L730: pop edi ; Line 24 mov eax, 1 pop esi ; Line 27 ret 0 $L729: ; Line 23 xor eax, eax $L721: pop edi pop esi ; Line 27 ret 0 ?a@@YAHHHH@Z ENDP ; a _TEXT ENDS
Здравствуйте, гость!
Гостевой функционал сайта ограничен. Для полноценной работы зарегистрируйтесь, пожалуйста.