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
в чем подвох?
в том что это не C#
Ayrat
оптимизация хвостовой рекурсии делается джитом
Ayrat
для callvirt + ret call + ret call + pop + ret callvirt + pop +ret патернов
Ayrat
ИЛИ она может делаться через tail команду
Ayrat
что не умеет C#, а умеет F#
Ayrat
F# компилятор явно эмитит команду для джита - сделай здесь хвостовую рекурсию
Ayrat
потому что джит может не соптимизировать хвостовую рекурсию, это вообще необязательно
Ayrat
а tail обязан.
Vasily
А багу про тейл кол и ноп поправили?
Vasily
Гопак должен страдать
Ayrat
Я свой ишью очень общо назвал - nop в релиз коде
Ayrat
убрать конкретно этот nop перед ret ИЗИ
Ayrat
а убрать все прочие нопы - тяжко
habib
для callvirt + ret call + ret call + pop + ret callvirt + pop +ret патернов
call bool _::even(int32) IL_000d: ret } // end of method _::odd - оптимизирует?
Ayrat
call bool _::even(int32) IL_000d: ret } // end of method _::odd - оптимизирует?
у тебя немного странное форматирование, но подозреваю что это две последовательные команды)
Ayrat
да, должен
Ayrat
ну как должен
Ayrat
МОЖЕТ
Ayrat
должен он только в случае tail оптимизировать
habib
да я с щарплаба дергаю
Ayrat
был бы там tail call ret
habib
это для сишарповкого кода
habib
был бы там tail call ret
а для фшапровского так, да
Ayrat
Сишарп нихуя не делает для хвостовой рекурсии, поэтому её в языке нет
Ayrat
то что она внезапно иногда джитом оптимизируется - на это нельзя полагаться
Ayrat
это чисто его приколы
habib
понятно
habib
но
habib
Сишарп нихуя не делает для хвостовой рекурсии, поэтому её в языке нет
а что должен сишарп делать для хвостовой рекурсии?
habib
просто, для нее особо ничего не надо делать
habib
ща найду
Ayrat
а что должен сишарп делать для хвостовой рекурсии?
определять её на уровне распарса AST в IL и эмтить tail команду для джита
Ayrat
т.к. tail по спеке обязан делать хвостовую рекурсию, мы получаем гарантию хвостовой и в языке C#
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
забавно)
Vladislav
а убрать все прочие нопы - тяжко
ты им свои исправленные скинь хотя бы)
habib
это уже похоже на правду
Ayrat
ты им свои исправленные скинь хотя бы)
Да там исправлений-то, -2 строчки и все исправления
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 "замораживает" вычисления - это очень круто. Можно почти как на хаскеле писать, с амортизированной сложностью
Bonart
не умеет linq в туплы
В туплы не умеет не linq а провайдеры linq для баз данных
Dr. Friedrich
В туплы не умеет не linq а провайдеры linq для баз данных
Ну да, какой-то конкретный провайдер для конкретной БД :)
Pavel
Ленивость круто иметь как инструмент. То что у нас lazy "замораживает" вычисления - это очень круто. Можно почти как на хаскеле писать, с амортизированной сложностью
даже неожиданно > let mutable i = 4;; val mutable i : int = 4 > let l = lazy i;; val l : Lazy<int> = Value is not created. > i <- 10;; val it : unit = () > l.Value;; val it : int = 10
Ayrat
Вообще космос
Pavel
это то я видел
Pavel
я вот что мутабельное заполнение отложится не ожидал
Ayrat
Ну, тогда вот. Никакие экспрешны справа от лейзи не вычисляются
Ayrat
И мемоизируются!
Bonart
Ayrat
И гарантированно один раз вычисляются