LINUX.ORG.RU

Typed Racket. Что я делаю не так?

 ,


0

2

Хочу ускорить пример кода

(define (test)
  (letrec ([oddp (lambda (x)
                   (if (zero? x) #f
                       (evenp (sub1 x))))]
            [evenp (lambda (x)
                    (if (zero? x) #t
                        (oddp (sub1 x))))])
    (oddp 400000000)))

потому как выполняется он 30-35 секунд, в то время как эквивалентный на sbcl:

(defun test ()
           (labels ((oddp1 (x)
                      (if (zerop x) nil
                          (evenp1 (1- x))))
                    (evenp1 (x)
                      (if (zerop x) t
                          (oddp1 (1- x)))))
             (oddp1 400000000)))

всего лишь 4-8 секунд, а в варианте

(defun test ()
           (labels ((oddp1 (x) (declare (fixnum x))
                      (if (zerop x) nil
                          (evenp1 (1- x))))
                    (evenp1 (x) (declare (fixnum x))
                      (if (zerop x) t
                          (oddp1 (1- x)))))
             (oddp1 400000000)))
вообще 2-3 секунды.

Пытаюсь сделать типы в Racket:

(define (test)
  (letrec: ([oddp : (Fixnum -> Boolean) 
                  (λ: ([x : Fixnum])
                   (if (zero? x) #f
                       (evenp (sub1 x))))]
            [evenp : (Fixnum -> Boolean) 
                   (λ: ([x : Fixnum])
                    (if (zero? x) #t
                        (oddp (sub1 x))))])
    (oddp 400000000)))

Получаю ошибку: Type Checker: Expected Fixnum, but got Integer in: (sub1 x)

Что я делаю не так и как правильно?

★★★★★

Последнее исправление: monk (всего исправлений: 2)

racket/fixnum

Попробуйте fx- из пакета racket/fixnum

#lang typed/racket/base

(require racket/fixnum)

(define (test)
  (letrec: ([oddp : (Fixnum -> Boolean) 
                  (λ: ([x : Fixnum])
                   (if (zero? x) #f
                       (evenp (fx- x 1))))]
            [evenp : (Fixnum -> Boolean) 
                   (λ: ([x : Fixnum])
                    (if (zero? x) #t
                        (oddp (fx- x 1))))])
    (oddp 400000000)))

netrino
()
Ответ на: racket/fixnum от netrino

Заработало. Стало 22 секунды. Правда всё равно как-то сильно медленно. В 10 раз медленней, чем SBCL. Может ему ещё что-то можно сделать?

В SBCL, если поставить (declaim (debug 0) (speed 3)), получаю 0.35 секунды. В drracket что-нибудь такое же есть?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Попробуйте запустить в консоли, DrRacket довольно сильно тормозит исполнение. У меня выполняется примерно за полторы секунды на довольно старом ноутбуке.

netrino
()
Ответ на: racket/fixnum от netrino

«These safe operations are generally no faster than using generic primitives like +.»

anonymous
()
Ответ на: комментарий от anonymous

anonymous

«These safe operations are generally no faster than using generic primitives like +.»

У них тип подходящий, в отличие от. И да, для более быстрого (но менее безопасного) есть racket/unsafe/ops с unsafe-fx+, unsafe-fx-, etc.

anonymous

Кстати, почему так? И как это лечится?

Точно не помню, не так давно это обсуждали на #racket, то ли уборщик мусор один и на доктора и на интерпретатор, то ли что-то связанное с отладкой. Утверждать ничего не буду, т.к. не знаю наверняка.

netrino
()
Ответ на: комментарий от netrino

Попробуйте запустить в консоли,

В консоли работает. Кстати, нашёл комбинацию, при которой typed дал ускорение в два раза: надо ставить не Fixnum, a Nonnegative-Fixnum. Тогда отрабатывает за секунду. В консоли.

А может ещё какой-нибудь метод чуть ускорить есть? Типа (declaim (debug 0) (speed 3)). А то три раза разницы на простом цикле — много.

monk ★★★★★
() автор топика
Ответ на: комментарий от netrino

то ли что-то связанное с отладкой

Нашёл! Надо в выборе языка зайти в подробно и отключить отладку. Теперь хотелось бы найти, вдруг через #lang можно так сделать.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Скорее всего нет, в выборе языка регулируются параметры, такие как current-compile - для компиляции с отладочной информацией или без. То есть это настройка на компилирующей стороне и не принимает во внимание исходник. http://docs.racket-lang.org/drracket/module.html?q=debug

netrino
()
Ответ на: комментарий от monk

Вот такое читал? http://docs.racket-lang.org/guide/performance.html

Typed racket это надстройка над typed операциями (типа fx+, fl+, etc) обычного racket'а. Возможно typed racket использует safe версии операций, которые соответственно содержат рантайм проверку типов. Попробуй не typed/racket а unsafe операции во все поля.

asvil
()
Ответ на: комментарий от asvil

typed racket использует unsafe операции.

Это понятно. Но, в отличие от простого unsafe-*, ещё и статическую типизацию немного обеспечивает.

Вот только со скоростью неясно.

Проверил на Windows:

SBCL:

(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defun test ()
           (labels ((oddp1 (x) (declare (fixnum x))
                      (if (zerop x) nil
                          (evenp1 (1- x))))
                    (evenp1 (x) (declare (fixnum x))
                      (if (zerop x) t
                          (oddp1 (1- x)))))
             (oddp1 400000000)))
(time (test))
Evaluation took:
  0.219 seconds of real time

Racket:

(require racket/unsafe/ops)

(define (test)
  (letrec ([oddp (λ (x) (if (zero? x) #f
                       (evenp (unsafe-fx- x 1))))]
            [evenp (λ (x)
                    (if (zero? x) #t
                        (oddp (unsafe-fx- x 1))))])
    (oddp 400000000)))
(time (test))
cpu time: 1109 real time: 1110 gc time: 93

В 5(!) раз разница.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Разница в следующем

SBCL:

* (declaim (optimize (speed 3) (safety 0) (debug 0)))

* (defun test ()
           (labels ((oddp1 (x) (declare (fixnum x))
                      (if (zerop x) nil
                          (evenp1 (1- x))))
                    (evenp1 (x) (declare (fixnum x))
                      (if (zerop x) t
                          (oddp1 (1- x)))))
             (oddp1 400000000)))

TEST
* (time (test))

Evaluation took:
  0.734 seconds of real time
  0.671875 seconds of total run time (0.671875 user, 0.000000 system)
  91.55% CPU
  1,821,519,757 processor cycles
  0 bytes consed

NIL
* (time (test 'huy))
; in: LAMBDA NIL
;     (TEST 'HUY)
;
; caught STYLE-WARNING:
;   The function was called with one argument, but wants exactly zero.
;
; compilation unit finished
;   caught 1 STYLE-WARNING condition

Evaluation took:
  0.732 seconds of real time
  0.671875 seconds of total run time (0.671875 user, 0.000000 system)
  91.80% CPU
  1,815,372,181 processor cycles
  0 bytes consed

NIL
*

Racket:

Добро пожаловать в DrRacket, версия 5.3.3 [3m].
Язык: racket [выбранный].
> (require racket/unsafe/ops)
> (define (test)
  (letrec ([oddp (λ (x) (if (zero? x) #f
                       (evenp (unsafe-fx- x 1))))]
            [evenp (λ (x)
                    (if (zero? x) #t
                        (oddp (unsafe-fx- x 1))))])
    (oddp 400000000)))
> (time (test))
cpu time: 1188 real time: 1267 gc time: 0
#f
> (time (test 'huy))
test: arity mismatch;
 the expected number of arguments does not match the given number
  expected: 0
  given: 1
  arguments...:
   'huy
> 
схема при каждом функциональном вызове ОБЯЗАНА проверить, является ли head функцией и принимает ли она нужное количество аргументов. В racket так же гарантируется наличие корректного стектрейса и выпадение во внешний prompt. То есть это (safety 1) (debug 1) и ниже нельзя. Отсюда и просадка на ~30% в скорости вызова (он зато инлайнится, в отличии от SBCL). Почему у вас в 5 раз, я не знаю.

anonymous
()
Ответ на: комментарий от anonymous

У меня процессорных циклов сильно меньше. Может от версии SBCL и типа процессора, конечно, зависит

CL-USER> (time (test))
Evaluation took:
  0.219 seconds of real time
  0.218750 seconds of total run time (0.218750 user, 0.000000 system)
  100.00% CPU
  603,659,843 processor cycles

Проверка корректности test проводится 1 раз (причём SBCL тоже проверяет и пишет соответствующую ошибку).

То есть это (safety 1) (debug 1) и ниже нельзя.

Хм... я уже пару раз ронял racket. Например,

(define (boom)
  (for ([i (in-range 20)])
    (place pch (place-channel-get pch))))

(boom)

На DrRacket/win32 5.3.2 [3m] убивает его гарантированно

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

По скорости — ощущение такое, что JIT параллельно с кодом выполняется и не отключается.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Проверка корректности test проводится 1 раз

Это проверка test, а есть еще oddp и evenp, которые проверяются на каждом вызове.

(причём SBCL тоже проверяет и пишет соответствующую ошибку)

SBCL ничего не проверяет при (safety 0) (debug 0), просто либо отрабатывает либо падает с simple-error если попытаться обратиться к аргументу, которого не передали (если не обращаться - то все кушает и отрабатывает без ошибок, типа). Сообщение - это варнинг компилятора, он при компиляции а не при исполнении выскакивает.

У меня процессорных циклов сильно меньше. Может от версии SBCL и типа процессора, конечно, зависит

Видимо, так. Учитывая, что вариант на Racket у нас примерно одинаково выполняется, можно предположить, что SBCL делает какие-то оптимизации заточенные под ваш проц, а Racket - нет.

Хм... я уже пару раз ронял racket.

Если racket падает, то это либо внутренняя ошибка vm, с safety никак не связанная, и надо писать багрепорт, или (как у вас), out of memory, оно в данном случае и должно падать, в спеках это написано.

anonymous
()
Ответ на: комментарий от monk

Кстати да, у вас в коде нету выделения памяти, значит gc не должен работать, а у вас работает. Значит, что-то происходит в другом треде.

anonymous
()
Ответ на: комментарий от asvil

Что-то странное выдаёт

> (decompile test #:size 64)
00000000  8943FC            mov [ebx-0x4],eax
00000003  83C3FC            add ebx,byte -0x4
00000006  B8F707AF2F        mov eax,0x2faf07f7
0000000B  8943FC            mov [ebx-0x4],eax
0000000E  83C3FC            add ebx,byte -0x4
00000011  8B55E4            mov edx,[ebp-0x1c]
00000014  83C2FC            add edx,byte -0x4
00000017  8B0B              mov ecx,[ebx]
00000019  890A              mov [edx],ecx
0000001B  89D3              mov ebx,edx
0000001D  B901000000        mov ecx,0x1
00000022  B88C9687AB        mov eax,0xab87968c
00000027  8B00              mov eax,[eax]
00000029  E9A525D707        jmp dword 0x7d725d3

Я ведь верно понимаю, что «jmp dword 0x7d725d3» — очень далеко посылает?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Смещения неправильные, потому что ndisasm-у скармливается обычный поток байтов, без указания стартового адреса. Заджиченный код может быть там где-то по 0x7d72500 и лежит.

Нужно бы около 1024 байт (чтобы конечный ret вошёл в диапазон), ну и посмотреть аналогичный на sbcl.

asvil
()
Ответ на: комментарий от asvil

Дизассемблер 1024 байт на http://paste.org/62596

не вижу куска, похожего на цикл

Для сравнения код из SBCL (который самый быстрый):

; disassembly for TEST
; 24235BB8:       840500000021     TEST AL, [#x21000000]      ; no-arg-parsing entry point
;       BE:       B800105E5F       MOV EAX, 1600000000
;       C3:       EB0E             JMP L1
;       C5: L0:   85C0             TEST EAX, EAX
;       C7:       741C             JEQ L4
;       C9:       83E804           SUB EAX, 4
;       CC:       85C0             TEST EAX, EAX
;       CE:       740B             JEQ L2
;       D0:       83E804           SUB EAX, 4
;       D3: L1:   840500000021     TEST AL, [#x21000000]
;       D9:       EBEA             JMP L0
;       DB: L2:   BA27001022       MOV EDX, 571473959
;       E0: L3:   8BE5             MOV ESP, EBP
;       E2:       F8               CLC
;       E3:       5D               POP EBP
;       E4:       C3               RET
;       E5: L4:   BA0B001022       MOV EDX, 571473931
;       EA:       EBF4             JMP L3

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

не вижу куска, похожего на цикл

Потому что в схеме нет циклов. Там ТСО с заинлайненными вызовами, вот этот кусок:

0000004E  8B4508            mov eax,[ebp+0x8]
00000051  8B4D0C            mov ecx,[ebp+0xc]
00000054  8B5510            mov edx,[ebp+0x10]
00000057  83EC1C            sub esp,byte +0x1c
0000005A  8B7D14            mov edi,[ebp+0x14]
0000005D  8D9F64040000      lea ebx,[edi+0x464]
00000063  8B1B              mov ebx,[ebx]
00000065  8D348D00000000    lea esi,[ecx*4+0x0]
повторяется, соответственно сама функция (одна итерация) это приблизительно нечто вроде:
0000004E  8B4508            mov eax,[ebp+0x8]
00000051  8B4D0C            mov ecx,[ebp+0xc]
00000054  8B5510            mov edx,[ebp+0x10]
00000057  83EC1C            sub esp,byte +0x1c
0000005A  8B7D14            mov edi,[ebp+0x14]
0000005D  8D9F64040000      lea ebx,[edi+0x464]
00000063  8B1B              mov ebx,[ebx]
00000065  8D348D00000000    lea esi,[ecx*4+0x0]
0000006C  01D6              add esi,edx
0000006E  8975E4            mov [ebp-0x1c],esi
00000071  39D3              cmp ebx,edx
00000073  7412              jz 0x87
00000075  81F901000000      cmp ecx,0x1
0000007B  750A              jnz 0x87
0000007D  895DE4            mov [ebp-0x1c],ebx
00000080  83C3FC            add ebx,byte -0x4
00000083  8B32              mov esi,[edx]
00000085  8933              mov [ebx],esi
00000087  81F901000000      cmp ecx,0x1
0000008D  0F8545A00000      jnz dword 0xa0d8
00000093  8943FC            mov [ebx-0x4],eax
00000096  83C3FC            add ebx,byte -0x4
00000099  8B4304            mov eax,[ebx+0x4]
0000009C  83C41C            add esp,byte +0x1c
0000009F  5F                pop edi
000000A0  5E                pop esi
000000A1  5B                pop ebx
000000A2  5D                pop ebp
000000A3  C3                ret
000000A4  07                pop es
000000A5  0000              add [eax],al
000000A7  008827CFB4E0      add [eax-0x1f4b30d9],cl
000000AD  254AB40000        and eax,0xb44a
000000B2  0000              add [eax],al
000000B4  0000              add [eax],al
000000B6  0000              add [eax],al
000000B8  55                push ebp
000000B9  89E5              mov ebp,esp
000000BB  53                push ebx
000000BC  56                push esi
000000BD  57                push edi

если вы попытаетесь дизасемблировать больше кода, то там внизу будет серия последовательных джампов (заинлайненные возвраты с ТСО) и потом последний ret.

anonymous
()
Ответ на: комментарий от monk

Заинтересовало меня сравнение, поэтому сел я поиграться с рэкетом.

Итак у нас есть такой код:

(define (test)
  (letrec ([oddp (lambda (x)
                   (if (zero? x) #f
                       (evenp (sub1 x))))]
            [evenp (lambda (x)
                    (if (zero? x) #t
                        (oddp (sub1 x))))])
    (oddp 400000000)))

В данном случае при должных декларациях типов, всё развернётся в целочисленную арифметику, но дизассемблирование в рэкете не умеет считать размеры функций и не умеет комментировать происходящее. Поэтому я заменил арифметику на «с плавающей точкой», которые будут реализованы на sse и тем самым хорошо различимы.

Итак тестовый код:

#lang racket

(require racket/unsafe/ops)
(require disassemble)

(define (test)
  (letrec ([oddp (lambda (x)
                   (if (unsafe-fl= x 0.0) #f
                       (evenp (unsafe-fl- x 1.0))))]
            [evenp (lambda (x)
                     (if (unsafe-fl= x 0.0) #t
                        (oddp (unsafe-fl- x 1.0))))])
    (oddp 400000000.0)
    
    (decompile oddp #:size 1024)
    (printf "----~%")
    (decompile evenp #:size 1024)
    (printf "----~%")))

(test)

(decompile test #:size 1024)

Сначала можно глянуть чётам происходит на лисповом уровне:

raco make test.rkt
raco decompile test.rkt

У рэкета оказывается есть такой прикол как разворачивание циклов. Итоговый лиспокод:

(define-values
     (_test)
     (lambda ()
       '#(test #<path:/home/michael/tmp/test.rkt> 14 0 228 443 #f)
       '(flags: preserves-marks single-result)
       '(captures:
         (val/ref #%modvars)
         (|_decompile38.4:p@(lib "disassemble/main.rkt")|))
       (let ((oddp74 ?) (evenp75 ?))
         (begin
           (#%set!-rec-values
            (oddp74 evenp75)
            (lambda (arg0-76)
              '#(oddp #<path:/home/michael/tmp/test.rkt> 15 17 260 108 #f)
              '(flags: preserves-marks single-result)
              '(captures: (val/ref evenp75))
              (if (unsafe-fl= arg0-76 '0.0)
                '#f
                (let ((flonum79 (unsafe-fl- arg0-76 '1.0)))
                  (if (unsafe-fl= flonum79 '0.0)
                    '#t
                    (let ((flonum84 (unsafe-fl- flonum79 '1.0)))
                      (if (unsafe-fl= flonum84 '0.0)
                        '#f
                        (let ((flonum89 (unsafe-fl- flonum84 '1.0)))
                          (if (unsafe-fl= flonum89 '0.0)
                            '#t
                            (let ((flonum94 (unsafe-fl- flonum89 '1.0)))
                              (if (unsafe-fl= flonum94 '0.0)
                                '#f
                                (evenp75 (unsafe-fl- flonum94 '1.0))))))))))))
            (lambda (arg0-102)
              '#(evenp #<path:/home/michael/tmp/test.rkt> 18 19 389 110 #f)
              '(flags: preserves-marks single-result)
              '(captures: (val/ref oddp74))
              (if (unsafe-fl= arg0-102 '0.0)
                '#t
                (let ((flonum105 (unsafe-fl- arg0-102 '1.0)))
                  (if (unsafe-fl= flonum105 '0.0)
                    '#f
                    (let ((flonum110 (unsafe-fl- flonum105 '1.0)))
                      (if (unsafe-fl= flonum110 '0.0)
                        '#t
                        (let ((flonum115 (unsafe-fl- flonum110 '1.0)))
                          (if (unsafe-fl= flonum115 '0.0)
                            '#f
                            (let ((flonum120 (unsafe-fl- flonum115 '1.0)))
                              (if (unsafe-fl= flonum120 '0.0)
                                '#t
                                (let ((flonum125 (unsafe-fl- flonum120 '1.0)))
                                  (if (unsafe-fl= flonum125 '0.0)
                                    '#f
                                    (let ((flonum130
                                           (unsafe-fl- flonum125 '1.0)))
                                      (if (unsafe-fl= flonum130 '0.0)
                                        '#t
                                        (oddp74
                                         (unsafe-fl-
                                          flonum130
                                          '1.0)))))))))))))))))
           (begin
             (evenp75 '399999995.0)
             (|_decompile38.4:p@(lib "disassemble/main.rkt")|
              '1024
              '#t
              (#%sfs-clear oddp74)
              '#f
              '#f)
             (printf '"----~%")
             (|_decompile38.4:p@(lib "disassemble/main.rkt")|
              '1024
              '#t
              (#%sfs-clear evenp75)
              '#f
              '#f)
             (printf '"----~%"))))))
asvil
()
Ответ на: комментарий от asvil

Из кошмара на лисповом уровне теперь ясен машкод.

Только вот зачем он такое делает, если в итоге получается многократно медленнее?

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Только вот зачем он такое делает

Что именно?

Развёртывает циклы где не надо. И не объединяет взаимнорекурсивные с TCO функции в одну.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Развёртывает циклы где не надо.

Почему где не надо? Выигрыш в производительности на порядок - это «не надо»?

И не объединяет взаимнорекурсивные с TCO функции в одну.

Он не может. Это нарушение семантики языка.

anonymous
()
Ответ на: комментарий от anonymous

Выигрыш в производительности на порядок - это «не надо»?

Ну как же выигрыш, если по факту в 5 раз проигрыш? Я же тест привёл.

Он не может. Это нарушение семантики языка.

Интересно. Инлайнить функцию саму в себя для раскручивания списка можно, а также инлайнить одну функцию в другую уже нельзя. Это почему?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Объединил вручную.

(define (test) 
  (letrec ([oddp (lambda (x) 
                   (if (zero? x) #f 
                       (let ([x (unsafe-fx- x 1)]) 
                         (if (zero? x) #t (oddp (unsafe-fx- x 1))))))])
    (oddp 400000000)))

Теперь не в 5, а в 4 раза медленнее, чем SBCL. Хотя теперь отличие только в развёртке циклов.

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Ну как же выигрыш, если по факту в 5 раз проигрыш?

Ну как, обычно. Отключи инлйанинг и убедись, что будет значительно медленнее.

Я же тест привёл.

Где? В сравнении с sbcl? Ну так дело там не в инлайнинге, а в том, что код самой итерации в racket больше. gnu lightning - очень хуевый джит.

Интересно. Инлайнить функцию саму в себя для раскручивания списка можно, а также инлайнить одну функцию в другую уже нельзя.

Так они и инлайнятся одна в другую. Но это же ф-я рекурсивная, ее заинлайнить _полностью_ нельзя - только раскрутить на определенную длину. А дальше обычный функциональный вызов, который медленный. В SBCL функциональных вызовов нету вообще - то есть там не инлайнинг, там просто замена ф-й циклом, весьма специфичная оптимизация, которая сработала в одном единственном конкретном случае (тот факт, что зависимо от процессора, как бе намекает). То есть с точки зрения sbcl там вообще никаких odd и even нет, что для схемы было бы нарушением семантики.

anonymous
()
Ответ на: комментарий от monk

Отличие в том, что racket ф-ю вызывает (потому что обязана), а SBCL - не вызывает и рандомно падает. Если поставить глубину раскрутки цикла больше (то есть racket будет инлайнить глубже и вызывать ф-ю реже), то будет приближаться к SBCL.

anonymous
()
Ответ на: комментарий от anonymous

замена ф-й циклом, весьма специфичная оптимизация

Вообще то это стандарт для TCO. В конце функции вместо хвостового вызова делается JMP на начало.

А дальше обычный функциональный вызов, который медленный

Для TCO должен быть JMP. По крайней мере стек в Racket не заполняется, значит JMP он делает.

То есть с точки зрения sbcl там вообще никаких odd и even нет, что для схемы было бы нарушением семантики.

Почему нарушение семантики? Функции локальные, значит их можно инлайнить в тело основной функции. Функция evenp вызывается только из oddp, значит её тело можно встроить в oddp.

поставить глубину раскрутки цикла больше

подскажи параметр, хочу проверить, что будет в асме, если отключить.

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

racket ф-ю вызывает (потому что обязана), а SBCL - не вызывает и рандомно падает.

При каких это условиях он падает? Работает также стабильно.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Вообще то это стандарт для TCO.

Вообще-то нет. Ты видишь в словах tail call optimisation хоть что-то на счет циклов? Я тоже нет.

Для TCO должен быть JMP. По крайней мере стек в Racket не заполняется, значит JMP он делает.

«обычный функциональный вызов» - всмысле вызов в ракетке. То, что он хвостовой, - это дело десятое, это значит лишь то, что при этом вызове вместо дополнительного выделения места на стеке будет использоваться уже выделенное. Но сам вызов никуда не девается.

Почему нарушение семантики? Функции локальные, значит их можно инлайнить в тело основной функции.

Вот racket и инлайнит. Но до конца не заинлайнишь - ф-я то рекурсивная!

Интересно, кстати, какой код сделает SBCL, если например в вызове odd внутри even сделать (odd 1 2) или (odd)?

подскажи параметр, хочу проверить, что будет в асме, если отключить.

Отключить инлайнинг? Присвой сперва ф-ям #f, а потом сделай set! с нужными лямбдами.

anonymous
()
Ответ на: комментарий от monk

Например если передать лишний аргумент (оно скушает без вопросов) или вообще не передать аргумента (виснет).

anonymous
()
Ответ на: комментарий от anonymous

Ой. Действительно, ставлю safety 1 — получаю 1.7 секунды. Максимальная скорость только на safety 0, а там обработка ошибок только фатальных.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Я ж с самого начала сказал, что аналог ракеты без дебужных опций - это (safety 1) :)

anonymous
()
Ответ на: комментарий от monk

Racket всё делает правильно :-)

И флаг ему в руки - я рад за него. Но вот на критический участок, в правильности которого я уверен (допустим он покрыт тестами на 100%), в SBCL я могу поставить safety 0. Т.е. возможностей по оптимизации кода таки больше ;)

yyk ★★★★★
()
Ответ на: комментарий от yyk

Но вот на критический участок, в правильности которого я уверен (допустим он покрыт тестами на 100%), в SBCL я могу поставить safety 0.

Если участок реально критический, то всё равно будет лучше его сделать на Си или ассемблере и запускать через FFI. Будет ещё быстрее (почти всегда). Я в данном треде просто взял пример, который оптимизируется в код идентичный сишному. Но так везёт редко.

А если нет, то ставить на код в эксплуатации (safety 0) по-моему очень дурная идея. Вон Gtk при каждом вызове assert на класс полученного объекта делает.

И покрытия тестами на 100% не бывает. Даже для примитивной задачи типа «программа получает 3 числа, должна вернуть тип треугольника (равнобедренный/равносторонний/разносторонний)» по статистике 80% людей не могут построить 100% покрытие всех существенных вариантов. Ну и при (safety 0) можно получать такие веши:

* (declaim (optimize (speed 3) (debug 0) (safety 0)))

* (defun test (x) x)

TEST
* (test)

CORRUPTION WARNING in SBCL pid 28139(tid 3084876096):
Memory fault at 890550f8 (pc=0x9021901, sp=0xb778da68)
The integrity of this image is possibly compromised.
Continuing with fingers crossed.
monk ★★★★★
() автор топика
Ответ на: комментарий от yyk

И флаг ему в руки - я рад за него. Но вот на критический участок, в правильности которого я уверен (допустим он покрыт тестами на 100%), в SBCL я могу поставить safety 0. Т.е. возможностей по оптимизации кода таки больше ;)

Дело не в безопасности кода, а в сохранении семантики. Сторонний код, например, может в своей логике использовать тот факт, что ф-я бросает исключение, и перехватывать его. Если скомпилировать такой код с аналогом safety 0, то он не будет работать.

anonymous
()
Ответ на: комментарий от monk

будет лучше его сделать на Си или ассемблере и запускать через FFI

В том случае, если sbcl просто не в состоянии генерировать +/- аналогичный код - да. А так - что ошибка определения типа треугольника в CL, что в си - какая разница? В си я ещё больше могу логических ошибок наплодить ;) И я не ратую за весь код под safety 0, или даже большие куски - нет, только действительно критические часто вызываемые и отлаженные маленькие участки кода

Ну и при (safety 0) можно получать такие веши:

а ещё можно топором по голове или голыми руками за токопроводящие шины...

yyk ★★★★★
()
Последнее исправление: yyk (всего исправлений: 1)
Ответ на: комментарий от anonymous

Если скомпилировать такой код с аналогом safety 0, то он не будет работать.

Я говорил, что safety 0 надо применять направо и налево, совершенно не думая о последствиях? :-\

Я всего лишь о том, что наличие такой возможности - лучше чем её отсутствие. Использование вместо этого FFI - переломанные костыли, скрепленные скотчем

yyk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.