5 ms·
CL-USER> (defun f (x) (* x x)) F CL-USER> (f 2.0) 4.0 CL-USER> (disassemble 'f) ; disassembly for F ; Size: 37 bytes. Origin: #x1002B10BDC
by mahmud 10y ago
CL-USER> (defun f (x) (* x x))
F
CL-USER> (f 2.0)
4.0
CL-USER> (disassemble 'f)
; disassembly for F
; Size: 37 bytes. Origin: #x1002B10BDC
; BDC: 498B4C2460 MOV RCX, [R12+96] ; thread.binding-stack-pointer
; no-arg-parsing entry point
; BE1: 48894DF8 MOV [RBP-8], RCX
; BE5: 488BD3 MOV RDX, RBX
; BE8: 488BFB MOV RDI, RBX
; BEB: 41BBB0020020 MOV R11D, 536871600 ; GENERIC-*
; BF1: 41FFD3 CALL R11
; BF4: 488B5DF0 MOV RBX, [RBP-16]
; BF8: 488BE5 MOV RSP, RBP
; BFB: F8 CLC
; BFC: 5D POP RBP
; BFD: C3 RET
; BFE: 0F0B10 BREAK 16 ; Invalid argument count trap
NIL
*
- username223 10y agoThat "GENERIC-*" points to just one of the problems.
- mahmud 10y ago[Edit: Julia prides itself in being a fully generic language. From its paper: "Generic functions have appeared in several object systems in the past, notably CLOS [15] and Dylan [28]. Julia is distinguished from these in that it uses generic functions as its primary abstraction mechanism, putting it in the company of research languages like Diesel [10] and Cecil [9]." :-) ] Behold! CL-USER> (defun f (x) (declare (single-float x)) (* x x)) F CL-USER> (compile 'f) F NIL NIL CL-USER> (disassemble 'f) ; disassembly for F ; Size: 37 bytes. Origin: #x10048EACEC ; CEC: 498B4C2460 MOV RCX, [R12+96] ; thread.binding-stack-pointer ; no-arg-parsing entry point ; CF1: 48894DF8 MOV [RBP-8], RCX ; CF5: 0F28D1 MOVAPS XMM2, XMM1 ; CF8: F30F59D1 MULSS XMM2, XMM1 ; CFC: 660F7ED2 MOVD EDX, XMM2 ; D00: 48C1E220 SHL RDX, 32 ; D04: 4883CA19 OR RDX, 25 ; D08: 488BE5 MOV RSP, RBP ; D0B: F8 CLC ; D0C: 5D POP RBP ; D0D: C3 RET ; D0E: 0F0B10 BREAK 16 ; Invalid argument count trap NIL
- lomnakkus 10y agoBut now "f" is entirely monomorphic (rather than just having an automatic specialization for float), AFACIT.
- mahmud 10y agoYou can externalize Common Lisp type specialization with its own platform-specific file full of DEFTYPE and/or declaim forms. Read Henry Baker's paper on the CL-84 type inference engine. The intellectual ancestor of all modern dynamic PL optimization.
- lomnakkus 10y ago> You can externalize Common Lisp type specialization with its own platform-specific file full of DEFTYPE and/or declaim forms. I'm not really famililar with CL, so forgive the perhaps dumb question: Does what you mentioned permit the user to write the completely generic algorithm while guaranteeing that it'll be specialized/monomorphized automatically by the comiler/runtime[1] when possible? As understood it, the thing the PP cared about whether it was guaranteed to occur and that it be automatic.
- junke 10y agoYes. http://www.sbcl.org/manual/#Declarations-as-Assertions http://www.sbcl.org/manual/#Declarations-as-Assertions
- lomnakkus 10y agoI don't think that's sufficient. AFAICT it at least implies runtime checks? [1] (I'm aware that these checks can be made very cheap, like JIT compilers do, but they're not zero-cost like compile-time monomorphization would be.) [1] I mean if you want to avoid segfaults w/e.g. "No type checks".
- 10y ago
- tomp 10y agoJulia does it an order of magnitude better, though. A generic `f` will probably be similar to the Common Lisp one. However, as soon as you use `f` in a context where types are known (e.g. if you use multimethods to overload a function that calls `f`), it will be specialized and JIT-compiled for the specific type (int, float, matrix, ...), with no extra work for the programmer.
- lispm 10y agoIn the 'Lisp World' JIT compiler are mostly not used. The main approaches are: * program compilation, where the object system can declare parts as non-extensible * a runtime compiler, which can compile methods See for example for a recent attempt to statically compile methods and handing the types to the optimizing compiler (which also uses type inference): https://github.com/guicho271828/inlined-generic-function/ https://github.com/guicho271828/inlined-generic-function/
- baldfat 10y agoIn the Lisp World, Racket has a great JIT which is optional. https://docs.racket-lang.org/guide/performance.html https://docs.racket-lang.org/guide/performance.html Racket is a beautiful thing.
- lispm 10y agoRacket is Racket. CLISP also can use a JIT - it uses GNU Lightning for runtime native code generation (https://www.gnu.org/software/lightning/ https://www.gnu.org/software/lightning/). Anyway the Racket JIT seems to be a bit limited: > The JIT compiler works incrementally as functions are applied, but the JIT compiler makes only limited use of run-time information when compiling procedures, since the code for a given module body or lambda abstraction is compiled only once. The JIT’s granularity of compilation is a single procedure body, not counting the bodies of any lexically nested procedures.
- junke 10y agoIf I understand correctly, functions are inlined[0] (not saying this is bad, just different). If you declare functions as inline then you can get rid of the dispatching code in contexts where the type is known in advance. [0] https://github.com/JuliaLang/julia/issues/265 https://github.com/JuliaLang/julia/issues/265