LINUX.ORG.RU

Clasp, одна из новых реализаций CL, всего лишь в четыре раза медленнее, чем C++

 , , , ,


4

3

Новая реализация компилятора CClasp, базирующегося на Cleavir от Robert Strandh, без оптимизаций, всего лишь в четыре раза медленнее, чем C++. Ожидается, что с добавлением вывода типов производительность генерируемого кода с CClasp, должнo еще прибавить в скорости выполнения.

В приведенной таблице, также есть сравнение производительности генерируемого кода с SBCL (еще одна из активных реализаций CL) и Python.

Основной особенностью Clasp, среди других реализаций Common Lisp, является тесная интеграция с C++ и использование LLVM.

Подробности: https://drmeister.wordpress.com/2015/07/30/timing-data-comparing-cclasp-to-c-...

Ответ на: комментарий от multimethod

всю эту функциональность, которая плохо или не работает в CL использовать для biding-а к GTK

К GTK из этого всего относились [ul] [li] пакеты и имена обобщённых функций — в text-iter нельзя использовать метод editable, так как он уже есть в text-view, а число аргументов разное. Можно было сделать по пакету на виджет, но, по мне, это совсем неудобно. [li] финалайзеры, слабые ссылки — в SBCL не всё хорошо, но здесь реальных проблем не было — где не хватало, использовал из Gtk. [li] Порча чужих пакетов — redefine интерфейса CFFI я уже приводил [/ul]

Остальное — к биндингу напрямую не относится. Хотя, например, как без call/cc сделать

...
(when (get-password) ; здесь открывается окно ввода пароля
  (open-admin-window))
...
не блокируя все окна, я не представляю. Точнее, представляю: будет две функции: одна по запрос пароля, а вторая, передаваемая колбэком — с новым окном.

В C# для борьбы с лапшой из колбэков придумали async/await, а в CL до сих пор считается, что продолжения не нужны.

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

Этот код делает всё тоже самое.

Нет. Он открывает не ту базу, как минимум (у тебя (merge-pathnames ".cl-sophia-storage/" (user-homedir-pathname)), а не "./storage"). И нет заглушек для обработки ошибок. И нет аналога printf.

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

Хм. Понял. Ну тут надо понимать, что x-ом может быть it форма, а экспандишь в aif. В данном случае всегда нужно страховаться:

(defmacro fwrite (stream x)
  (once-only (x)
    `(aif ,stream (format it "~a" ,x) nil)))
Синтетическая проблема. Если мы генерируем формы с анафорическими макросами, нужно взять за правило вычислять заранее. Как это сделать красивее на Racket?

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

Нет. Он открывает не ту базу, как минимум (у тебя (merge-pathnames ".cl-sophia-storage/" (user-homedir-pathname)), а не "./storage").

Придерешься. Очевидно, что это не влияет на смысл. Какая разница какой путь, чёрт побери. Я сделал другой по умолчанию, что бы не пересекаться с примерами используемыми не в cl-sophia.

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

На мой взгляд, сущности env, ctl - лишние

Покажи, как открыть две базы, и, если заданный ключ имеет значение только в одной, то скопировать его значение из другой. Для хэшей было бы:

(multiple-value-bind (val1 has1) (gethash key hash1)
  (multiple-value-bind (val2 has2) (gethash key hash2)
    (cond
      ((and has1 (not has2)) (setf (gethash key hash2) val1))
      ((and has2 (not has1)) (setf (gethash key hash1) val2)))))

monk ★★★★★
()
Ответ на: комментарий от monk
(with-named-databases
    ((db1 "db1")
     (db2 "db2"))
  (let ((val1 ($ key db1))
        (val2 ($ key db2)))
    (cond
      ((and val1 (not val2))
       (setf ($ key db2) val1))
      ((and val2 (not val1))
       (setf ($ key db1) val2)))))

Так как база может содержать только строки, nil точно определяет отсутствие данных по ключу.

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

Если нужно с разными storage-ами работать, то придётся вот так сделать:

(let ((*path* "<path-to-starge-1>"))
  (with-named-databases ((db1 "db1"))
    (let ((*path* "<path-to-storage-2>"))
      (with-named-databases ((db2 "db2"))
        ...))))
Там есть фича, если работаешь с несколькими базами в одним storage в контексте одного environment-а, то доступны транзакции через несколько открытых баз. with-named-databases всё это реализует.

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

Так как база может содержать только строки

База может содержать все что угодно:

sp_set(o, "value", 666, sizeof(int));
anonymous
()
Ответ на: комментарий от multimethod
(require racket/stxparam) 
(define-syntax-parameter it 
  (lambda (stx) 
    (raise-syntax-error (syntax-e stx) "can only be used inside aif"))) 
 
(define-syntax-rule (aif condition true-expr false-expr) 
  (let ([tmp condition]) 
    (if tmp 
        (syntax-parameterize ([it (make-rename-transformer #'tmp)]) 
          true-expr)
        false-expr)))

(define-syntax-rule (fwrite str x)
  (aif str (fprintf it "~a" x) #f))

(aif 3 (fwrite (current-output-port) it) #f) ; => 3
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от anonymous

У меня только строки. Добавлю, когда sophia перестанет быть сырой.

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

Да. Но часто нужно просто работать с одной базой, поэтому опциональный аргумент с хендлером базы по умолчанию - динамическая переменная, которую устанавливает with-database. И не нужно запариваться и перекидывать каждый раз имя.

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

Ну тут надо понимать, что x-ом может быть it форма, а экспандишь в aif. В данном случае всегда нужно страховаться:

Проблема не в том, что it много раз вычисляется, проблема в том, что aif внутри fwrite переопределяет лишний it.

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

Ее нельзя без гигиены избежать. Любой анафорический макрос в CL ломается, причем ломается элементарно, и никакие костыли помочь не могут. В этом и есть смысл фразы «нельзя написать нормальный aif».

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

Ее нельзя без гигиены избежать

Можно, но сложно.

Например так:

(defmacro aif (test then &optional else)
  (let ((it (gensym)))
    `(let ((,it ,test))
        (if ,it ,(maptree (lambda (x) (if (eq x 'it) it x))
                          then)
                ,else))))

Для совсем правильного aif надо вместо maptree какой-то map, который обходит всё, кроме голов списков.

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

Сказки. Вполне возможно. Можно попробовать с CLtL2:

(eval-always
  (defparameter **uniq-it**
    nil))

(defmacro non-anaphoric (form)
  (unless **uniq-it**
    (error "..."))
  (macroexpand-all `(symbol-macrolet ((it ,**uniq-it**))
                      ,form)))

(defmacro aif% (test then &optional else &environment env)
  (if (variable-information 'it env)
      `(let ((it ,test))
         (if it ,then ,else))
      (with-unique-names (uniq-it)
        `(let* ((,uniq-it ,test)
                (it ,uniq-it))
           (compiler-let ((**uniq-it** ',uniq-it))
             (if it ,then ,else))))))


(defmacro fwrite% (stream x)
  `(aif% ,stream (format it "~a" (non-anaphoric ,x)) nil))


(fwrite% t 3)                            ; 3
(aif% 3 (fwrite% t it))                  ; 3
Но тут придётся явно указывать non-anaphoric в макросах генерирующих aif. Но это может и не плохо. Есть контроль. Можем выбирать будет fwrite анафорическим или нет.

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

Ерунду какую-то с macroexpand-ом написал. Вот так:

(defmacro non-anaphoric (form)
  (unless **uniq-it**
    (error "..."))
  `(let ((it ,**uniq-it**))
     (declare (ignorable it))
     ,form))

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

Любой анафорический макрос в CL ломается, причем ломается элементарно, и никакие костыли помочь не могут.

Я не считаю, что это проблема анафорических макросов. Они так и работают. fwrite получается, тоже становится анафорическим и имеет собственный it в скоупе. Это нормально. Но как демонстрация возможностей гигиены и Racket - нормально.

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

fwrite получается, тоже становится анафорическим и имеет собственный it в скоупе. Это нормально.

Это сильно ограничивает применение анафорических макросов в макросах.

Но как демонстрация возможностей гигиены и Racket - нормально.

На самом деле, гигиена в Scheme появилась по необходимости из-за общего пространства имён функций и переменных.

В Common Lisp не задумываешься, но 99% макросов жёстко зависят от того, что в данном контексте не переопределён никакой символ из пакета CL. В Scheme, если я явно не использую list в какой-нибудь функции, я могу написать (let ((list (lambda () '(1 2 3)))) ...) и ничего плохого гарантированно не случится. В CL подбирая имена для (flet ...) всегда тщательно вспоминаю, не пересекся ли я случайно с чем-нибудь из CL или других пакетов в списке использованных.

С тем же CLOS. Нельзя назвать слоты параллелограмма width и length. Точнее можно, но в пределах пакета надо будет замаскировать CL:LENGTH и не забывать его писать с префиксом. Или определить (defmethod length ((list list)) (cl:length list)).

Таких мелких неудобств много. Каждое по отдельности вроде мелкое, но когда их накапливается много, они мешают.

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

Это нормально.

Нет, это ненормально, потому что у тебя нет возможности контроля над тем, сломается макрос или нет. Если, например, пользователю fwrite можно в документации сказать, что там внутрях aif, а потому не надо использовать в теле it, и это еще ок (хоть абстаркция и течет, ну да ладно), то какой-то макрос из другой библиотеки может раскрываться с it и автор этого макроса конечно не обязан да и не может знать о том что где-то it используется как анафора, а потмоу и в документации указать это, в общем, не может.

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

В одной форме может быть несколько it с _разными_ связываниями, например (+ it it), где первый it будет 1, а второй - 2 (например они из экспанда разных макрсоов появились). Так что работать не будет.

Ну и в общем-то ты уже занялся тем, что делаешь экспанд руками, если доделаешь все без багов и как надо, то и получится гигиена.

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

На самом деле, гигиена в Scheme появилась по необходимости из-за общего пространства имён функций и переменных.

На самом деле гигиена - это просто свойство, которое заключается в том, что экспанд не нарушает альфа-эквивалентность. Гигиенический экспанд - это такой экспанд, что альфа-эквивалентные изначально программы остаются альфа-эквивалентными на каждом шаге экспанда.

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

будет какой-нибудь макрос yoba, который раскрывается в it, все сломается.

С чего это? Макросы в CL раскрываются сначала внешние. То есть сначала (aif (test) (cons (yoba) it) (yoba)) раскроется в (let ((#:gensym-1 (test))) (cons (yoba) #:gensym-1) (yoba)), а потом уже будет раскрываться yoba. Но к этому моменту в контексте никакого it уже нет.

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

Ну так потом йоба раскроется и у тебя останется форма (cons it #:gensym-1), it, как ты верно заметил, в контексте нет (или есть в контексте внешнем по отношению к aif) и с-но ломается.

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

Так ты хочешь, чтобы йоба раскрывалось в тот же it, который используется в aif? Тогда aif должен знать про йобу (и тогда может явно её раскрывать перед заменой it). Причём в Scheme/Racket тоже самое: надо явно указывать, что it из aif и it из йобы один и тот же.

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

Тогда aif должен знать про йобу (и тогда может явно её раскрывать перед заменой it).

но на практике же он не знает.

Причём в Scheme/Racket тоже самое: надо явно указывать, что it из aif и it из йобы один и тот же.

Явно указывать не надо, это происходит автоматом. Если мы определили syntax-parameter и потом в соответствующем лексическом контексте, где it связано как параметр, определим макрос у которого под #' будет it, то это it будет иметь соответствующее связывание.

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

Если мы определили syntax-parameter

Ну тогда надо делать что-то вроде

(defmacro aif (test then &optional else)
  (let ((it (gensym)))
    `(let ((,it ,test))
        (if ,it ,(subst it 'it (recurse-macroexpand then))
                ,else))))

При раскрытии внутреннего AIF, внутри него IT заменится на генсим, а если макрос возвращает IT как есть, то ближайшиё AIF его заменит.

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

Да, но тут есть момент. macroexpand проходит в другом контексте нежели при раскрытии формы then обычным образом. Это можно повлечь за собой серьёзные проблемки.

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

В CL подбирая имена для (flet ...) всегда тщательно вспоминаю

Это смотря какой CL. На сколько мне известно, нонче в ходу LW и SBCL. Не знаю как там с LW дела обстоят (никак не доходят руки купить и поюзать), а вот в SBCL package locks.

(flet ((list () t))
  (list))

;; Execution of a form compiled with errors.
;; Form:
;;   (FLET ((LIST ()
;;          T))
;;   (LIST))
;; Compile-time error:
;;   Lock on package COMMON-LISP violated when binding LIST as a local function
;; while in package COMMON-LISP-USER.
;; See also:
;;   The SBCL Manual, Node "Package Locks"
;;   The ANSI Standard, Section 11.1.2.1.2
;;    [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

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

или других пакетов в списке использованных.

С этим уже сложнее. Некоторые параноидально используют <package-name>:<symbol>. Тоже выход, когда есть опасения.

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

Таких мелких неудобств много. Каждое по отдельности вроде мелкое, но когда их накапливается много, они мешают.

Да. Конечно. Но это смотря с чем сравнивать. Если с Racket, то, возможно, что Racket действительно удобней и грамотней. Всё таки CL достаточно старый язык. Но вот если сравнивать с другой динамкой: Python, Ruby, Perl, Tcl... То CL в разы удобнее и мощнее.

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

(subst it 'it (recurse-macroexpand then))

Это альфа-конверсия, а альфа-конверсия сохраняет семантику в раскрытой программе только в гигиенических системах :)

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

when-let не решает проблемы, еще раз.

а просто понимать это

У тебя нет способа понимать это. Аиф может появиться через 10 макровызовов.

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

when-let не решает проблемы, еще раз.

Почему это не решает? Это отказ от генерации анафорических макросов в том случае, если ожидается поведение как в fwrite.

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

У тебя нет способа понимать это.

Понимать то, как ведут себя анафорические макросы в CL. Какие ещё способы?

Аиф может появиться через 10 макровызовов.

Может. Поэтому лучше не генерировать анафорические макросы.

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

Почему это не решает? Это отказ от генерации анафорических макросов в том случае, если ожидается поведение как в fwrite.

А откуда ты знаешь какое поведение ожидается?

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

Может. Поэтому лучше не генерировать анафорические макросы.

Вот к чему и пришли - единственный способ сделать так, чтобы оно работало, просто отказаться от раскрытия в анафорические макросы. А лучше и вовсе их не писать :)

Понимать то, как ведут себя анафорические макросы в CL.

Ну так что это даст, если ты не знаешь, что у тебя там внутре анафорические макросы?

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

Некоторые параноидально используют <package-name>:<symbol>

Писать COM.INFORMATIMAGO.COMMON-LISP.CESARUM.ARRAY:ARRAY-EQUAL-P не всегда удобно.

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

профессиональный лиспер

Архимаг никогда лиспером не был - он тайный питонист. Он сам неоднократно заявлял, что в лиспе для него главное возможность интерактивной разработки.

Ловепятьан - латентный сишарпер, несмотря на то, что прочитал всю гиперспеку и извращался с МОПом

Вообще лисповый код я видел у свизарда, мв и пары анонимусов в ЖЖшке. Есть ещё дмитрий_вк, чинивший СБЦЛ. Остальные или не выкладывают код из-за лицензии (как Oxdeadbeef), или не понимают суть лиспа.

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

чем крупнее проект, тем дороже становится проект на CL по сравнению с другими языками.

С точностью до наоборот. На малых проектах лисп проигрывает из-за многословности, а на больших выигрывает за счёт лёгкости введения абстракций и уменьшении количества программистов.

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

Это сильно ограничивает применение анафорических макросов в макросах.

И в обычном языке, в чуть более сложном предложении анафора может вводить неоднозначность, а ты хочешь в ЯП.

Суть анафоры в том, что лень писать (let ((it ... А дальше начинаются проблемы с выяснением тот ли это it или другой. Так сделай просто именованный it, раз жить не можешь без анафор

(defmacro aif (n-it test then &optional else)
  `(let ((,n-it ,test))
     (if ,n-it ,then ,else)))

Если с пространством имён (блокировка символов из CL, декларация никнеймов) есть реальные неудобства, то желание иметь гигиенические анафорические макросы - извращения эстета.

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

Писать COM.INFORMATIMAGO.COMMON-LISP.CESARUM.ARRAY:ARRAY-EQUAL-P не всегда удобно.

Слегка «через жопу», но решаемо (хотя, лучше было бы как-нибудь через никнеймы)

CL-USER> (setf (symbol-function 'cl-l) #'cl:length)
#<FUNCTION LENGTH>
CL-USER> (cl-l (list 1 2 3))
3
anonymous
()
Ответ на: комментарий от anonymous

Архимаг никогда лиспером не был - он тайный питонист.

Ловепятьан - латентный сишарпер

А мне диагноз поставишь? В смысле, на тот момент когда я писал на CL. Код на github'е я показывал.

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

а на больших выигрывает за счёт лёгкости введения абстракций

Модульности нормальной нет. И абстракции часто текут (потому что инкапсуляции тоже нет и проще подкрутить чуть-чуть в потрохах, чем переделать правильно).

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

Это помогает пока программистов немного. Если четыре человека могут писать код, с той скоростью, что на Java надо двадцать, то всё круто. Четыре человека смогут понять друг друга сами. А если у тебя 400 программистов на Java и ты можешь потенциально заменить их сотней на лиспе, то уже не поможет: так как эта сотня поломает код друг другу и на решение конфликтов будет уходить слишком много времени.

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