サイトー
habib
всегда думал, что в сишарпе нет оптимизации хвостовой рекурсии
например,
let rec odd x = if x = 0 then false else even <| x - 1
and even x = if x = 0 then true else odd <| x - 1
public static class _
{
public static bool odd(int x)
{
if (x == 0)
return false;
return even(x - 1);
}
public static bool even(int x)
{
if (x == 0)
return true;
return odd(x - 1);
}
}
генерят одинаковый асм код
_.odd(Int32)
L0000: push ebp
L0001: mov ebp, esp
L0003: test ecx, ecx
L0005: jnz L000b
L0007: xor eax, eax
L0009: pop ebp
L000a: ret
L000b: dec ecx
L000c: jnz L0015
L000e: mov eax, 0x1
L0013: jmp L001c
L0015: dec ecx
L0016: call dword [0x1d141f44]
L001c: pop ebp
L001d: ret
_.even(Int32)
L0000: test ecx, ecx
L0002: jnz L000a
L0004: mov eax, 0x1
L0009: ret
L000a: dec ecx
L000b: call _.odd(Int32)
L0010: ret
habib
в чем подвох?
Vasiliy
ну это же хорошо, не7
habib
да, хорошо
habib
просто неожиданно
habib
притом в фшарпоском ил-коде есть инструкция tail. - в общем, только в этом отличие между сишарповским и фшарповским кодом
Pavel
чет не похоже на оптимизацию то
habib
возможно
habib
я к тому, что обе версии генерят одинаковый асм
Ayrat
оптимизация хвостовой рекурсии делается джитом
Ayrat
для
callvirt + ret
call + ret
call + pop + ret
callvirt + pop +ret
патернов
Ayrat
ИЛИ она может делаться через tail команду
Ayrat
что не умеет C#, а умеет F#
Ayrat
F# компилятор явно эмитит команду для джита - сделай здесь хвостовую рекурсию
Ayrat
потому что джит может не соптимизировать хвостовую рекурсию, это вообще необязательно
Ayrat
а tail обязан.
Vasily
А багу про тейл кол и ноп поправили?
Ayrat
Vasily
Гопак должен страдать
Ayrat
Я свой ишью очень общо назвал - nop в релиз коде
Ayrat
убрать конкретно этот nop перед ret ИЗИ
Ayrat
а убрать все прочие нопы - тяжко
Ayrat
да, должен
Ayrat
ну как должен
Ayrat
МОЖЕТ
Ayrat
должен он только в случае tail оптимизировать
habib
да я с щарплаба дергаю
Ayrat
был бы там
tail
call
ret
habib
это для сишарповкого кода
Ayrat
Сишарп нихуя не делает для хвостовой рекурсии, поэтому её в языке нет
Ayrat
то что она внезапно иногда джитом оптимизируется - на это нельзя полагаться
Ayrat
это чисто его приколы
habib
понятно
habib
но
habib
habib
просто, для нее особо ничего не надо делать
habib
ща найду
Ayrat
т.к. tail по спеке обязан делать хвостовую рекурсию, мы получаем гарантию хвостовой и в языке C#
Ayrat
habib
я сделал выводы отсюда,
Джеральд Джей Сассман и Гай Льюис Стил мл. (см. Steele 1975) построили
интерпретатор Scheme с поддержкой хвостовой рекурсии. Позднее Стил показал, что хвостовая рекурсия
является следствием естественного способа компиляции вызовов процедур (Steele 1977).
Pavel
занятно. версия let rec odd2 x = match x with 0 -> false | _ -> even2 <| x - 1
and even2 x = match x with 0 -> true | _ -> odd2 <| x - 1 рожает не такой шизофренический код
Ayrat
т.е. компилятор должен понять - ага, здесь вызов функции внутри самой функции и он последний! А давай-ка сделаем здесь хвостовую рекурсию. Для этого у нас есть железобетонное обещание от джита что если я перед call дам ему команду tail, то он её оптимизнёт
Pavel
_.odd2(Int32)
L0000: test ecx, ecx
L0002: jz L000c
L0004: dec ecx
L0005: call dword [0x19d01f68]
L000b: ret
L000c: xor eax, eax
L000e: ret
_.even2(Int32)
L0000: test ecx, ecx
L0002: jz L000b
L0004: dec ecx
L0005: call _.odd2(Int32)
L000a: ret
L000b: mov eax, 0x1
L0010: ret
Pavel
я давно замечал что мачи более адекватные
Ayrat
забавно)
habib
habib
это уже похоже на правду
Vladislav
ну скинь
Vladislav
ну пап
Ayrat
Ну лан
Ayrat
Сегодня обещаю что скину
Ayrat
не работать же на работе
Pavel
так что как и раньше рекомендую забить на if
Pavel
let nu1 (x : obj) = match x with null -> false | _ -> true
let nu2 (x : obj) = x <> null
Pavel
_.nu1(System.Object)
L0000: test ecx, ecx
L0002: jz L000a
L0004: mov eax, 0x1
L0009: ret
L000a: xor eax, eax
L000c: ret
_.nu2(System.Object)
L0000: push 0x1ac6ec5c
L0005: xor edx, edx
L0007: call dword [0x1ac6ec30]
L000d: test eax, eax
L000f: setz al
L0012: movzx eax, al
L0015: ret
Dr. Friedrich
Алонзо Чёрч вообще ничего про типы, композицию и ленивость не подразумевал когда лямбда калькулус сделал, так что давайте забьём на это всё.
Dr. Friedrich
Ну, надо заметить, в лямбда-счислении есть вариант редукции, который связан с ленивыми вычислениями
Ayrat
Точно так же в нём и композицию изи сделать, да
Ayrat
и как оказалось в последствии - типы пришпандорить вообще в два счёта
Ayrat
но это не является необходимостью для определения лямбд
Ayrat
Ленивость круто иметь как инструмент. То что у нас lazy "замораживает" вычисления - это очень круто. Можно почти как на хаскеле писать, с амортизированной сложностью
Ayrat
Ayrat
Вообще космос
Pavel
это то я видел
Pavel
я вот что мутабельное заполнение отложится не ожидал
Ayrat
Ну, тогда вот. Никакие экспрешны справа от лейзи не вычисляются
Ayrat
И мемоизируются!
Bonart
Ayrat
И гарантированно один раз вычисляются