alex-gutev / static-dispatch Goto Github PK
View Code? Open in Web Editor NEWStatic generic function dispatch for Common Lisp
License: MIT License
Static generic function dispatch for Common Lisp
License: MIT License
Are there plans to provide compiler notes - and/or respecting (optimize debug)
vs (optimize speed)
compilation options?
Edit: What I'd intend is that (optimize speed)
should directly cause an inline-declaration, while (optimize debug)
should cause a notinline declaration; I'm not objectively sure that's a good idea, but subjectively, it seems good.
Deprecated recursive use of (ASDF/OPERATE:OPERATE 'ASDF/LISP-ACTION:LOAD-OP
'("static-dispatch/test")) while visiting
(ASDF/LISP-ACTION:TEST-OP "static-dispatch/test") - please use proper
dependencies instead```
cl-arrows does not have a license file which makes it proprietary software.
Consider switching to another arrow macro library, such as arrows, arrow-macros, or (shameless plug) binding-arrows.
I am a bit confused over the disassembly I am getting with a static-dispatch-type
of inline
compared to function
. My intuition, as well as the documentation, says that inline should result in larger code size. However that is opposite to what I am seeing:
(defun test ()
(declare (optimize (speed 3))
(static-dispatch-type function ref))
(let ((a (make-aggregate :storage (make-array 3 :element-type 'single-float :initial-contents '(1.0 2.0 3.0)))))
(declare (type aggregate a))
(ref a 2)))
; disassembly for TEST
; Size: 282 bytes. Origin: #x53897740 ; TEST
; 740: 49896D28 MOV [R13+40], RBP ; thread.pseudo-atomic-bits
; 744: 498B4560 MOV RAX, [R13+96] ; thread.alloc-region
; 748: 4C8D5820 LEA R11, [RAX+32]
; 74C: 4D3B5D68 CMP R11, [R13+104]
; 750: 0F87EB000000 JNBE L4
; 756: 4D895D60 MOV [R13+96], R11 ; thread.alloc-region
; 75A: L0: C600CD MOV BYTE PTR [RAX], -51
; 75D: C6400806 MOV BYTE PTR [RAX+8], 6
; 761: 0C0F OR AL, 15
; 763: 49316D28 XOR [R13+40], RBP ; thread.pseudo-atomic-bits
; 767: 7402 JEQ L1
; 769: CC09 INT3 9 ; pending interrupt trap
; 76B: L1: 488BF8 MOV RDI, RAX
; 76E: F30F1005A2FDFFFF MOVSS XMM0, [RIP-606] ; [#x53897518]
; 776: F30F114001 MOVSS [RAX+1], XMM0
; 77B: F30F100599FDFFFF MOVSS XMM0, [RIP-615] ; [#x5389751C]
; 783: F30F114005 MOVSS [RAX+5], XMM0
; 788: F30F100590FDFFFF MOVSS XMM0, [RIP-624] ; [#x53897520]
; 790: F30F114009 MOVSS [RAX+9], XMM0
; 795: 4883EC10 SUB RSP, 16
; 799: BABF3D2250 MOV EDX, #x50223DBF ; :STORAGE
; 79E: B904000000 MOV ECX, 4
; 7A3: 48892C24 MOV [RSP], RBP
; 7A7: 488BEC MOV RBP, RSP
; 7AA: E813A4B8FC CALL #x50421BC2 ; #<FDEFN MAKE-AGGREGATE>
; 7AF: 488BC2 MOV RAX, RDX
; 7B2: 49896D28 MOV [R13+40], RBP ; thread.pseudo-atomic-bits
; 7B6: 4D8B5D60 MOV R11, [R13+96] ; thread.alloc-region
; 7BA: 498D4B20 LEA RCX, [R11+32]
; 7BE: 493B4D68 CMP RCX, [R13+104]
; 7C2: 0F8786000000 JNBE L5
; 7C8: 49894D60 MOV [R13+96], RCX ; thread.alloc-region
; 7CC: L2: 498D4B0B LEA RCX, [R11+11]
; 7D0: 41BC39020000 MOV R12D, 569
; 7D6: 4D0B65E0 OR R12, [R13-32]
; 7DA: 4C8961F5 MOV [RCX-11], R12
; 7DE: 49316D28 XOR [R13+40], RBP ; thread.pseudo-atomic-bits
; 7E2: 7402 JEQ L3
; 7E4: CC09 INT3 9 ; pending interrupt trap
; 7E6: L3: 4C8D2553FDFFFF LEA R12, [RIP-685] ; = #x53897540
; 7ED: 4C8961FD MOV [RCX-3], R12
; 7F1: 48894105 MOV [RCX+5], RAX
; 7F5: 488BD1 MOV RDX, RCX
; 7F8: BF17011050 MOV EDI, #x50100117 ; NIL
; 7FD: 488BF0 MOV RSI, RAX
; 800: 48C745F004000000 MOV QWORD PTR [RBP-16], 4
; 808: B908000000 MOV ECX, 8
; 80D: FF7508 PUSH QWORD PTR [RBP+8]
; 810: E9CDA3B8FC JMP #x50421BE2 ; #<FDEFN STATIC-DISPATCH.METHOD-FUNCTIONS::REF1>
; 815: 4D29DC SUB R12, R11
; 818: 4154 PUSH R12
; 81A: E8668D16FF CALL #x52A00585 ; CONS->R11
; 81F: E99FFDFFFF JMP #x538975C3
; 824: CC14 INT3 20 ; UNDEFINED-FUN-ERROR
; 826: 04 BYTE #X04 ; RCX(d)
; 827: 6A11 PUSH 17
; 829: E8578D16FF CALL #x52A00585 ; CONS->R11
; 82E: E99BFEFFFF JMP #x538976CE
; 833: 6A11 PUSH 17
; 835: E84B8D16FF CALL #x52A00585 ; CONS->R11
; 83A: E9C0FEFFFF JMP #x538976FF
; 83F: CC10 INT3 16 ; Invalid argument count trap
; 841: L4: 6A21 PUSH 33
; 843: E8CD8A16FF CALL #x52A00315 ; CONS->RNN
; 848: 58 POP RAX
; 849: E90CFFFFFF JMP L0
; 84E: L5: 6A21 PUSH 33
; 850: E8308D16FF CALL #x52A00585 ; CONS->R11
; 855: E972FFFFFF JMP L2
compared to inline
:
(defun test ()
(declare (optimize (speed 3))
(static-dispatch-type inline))
(let ((a (make-aggregate :storage (make-array 3 :element-type 'single-float :initial-contents '(1.0 2.0 3.0)))))
(declare (type aggregate a))
(ref a 2)))
; disassembly for TEST
; Size: 162 bytes. Origin: #x53895C7F ; TEST
; C7F: 49896D28 MOV [R13+40], RBP ; thread.pseudo-atomic-bits
; C83: 498B4560 MOV RAX, [R13+96] ; thread.alloc-region
; C87: 4C8D5820 LEA R11, [RAX+32]
; C8B: 4D3B5D68 CMP R11, [R13+104]
; C8F: 777C JNBE L2
; C91: 4D895D60 MOV [R13+96], R11 ; thread.alloc-region
; C95: L0: C600CD MOV BYTE PTR [RAX], -51
; C98: C6400806 MOV BYTE PTR [RAX+8], 6
; C9C: 0C0F OR AL, 15
; C9E: 49316D28 XOR [R13+40], RBP ; thread.pseudo-atomic-bits
; CA2: 7402 JEQ L1
; CA4: CC09 INT3 9 ; pending interrupt trap
; CA6: L1: 488BF8 MOV RDI, RAX
; CA9: F30F100597FFFFFF MOVSS XMM0, [RIP-105] ; [#x53895C48]
; CB1: F30F114001 MOVSS [RAX+1], XMM0
; CB6: F30F10058EFFFFFF MOVSS XMM0, [RIP-114] ; [#x53895C4C]
; CBE: F30F114005 MOVSS [RAX+5], XMM0
; CC3: F30F100585FFFFFF MOVSS XMM0, [RIP-123] ; [#x53895C50]
; CCB: F30F114009 MOVSS [RAX+9], XMM0
; CD0: 4883EC10 SUB RSP, 16
; CD4: BABF3D2250 MOV EDX, #x50223DBF ; :STORAGE
; CD9: B904000000 MOV ECX, 4
; CDE: 48892C24 MOV [RSP], RBP
; CE2: 488BEC MOV RBP, RSP
; CE5: E8D8BEB8FC CALL #x50421BC2 ; #<FDEFN MAKE-AGGREGATE>
; CEA: 488B4205 MOV RAX, [RDX+5]
; CEE: 488378F904 CMP QWORD PTR [RAX-7], 4
; CF3: 7625 JBE L3
; CF5: F30F104009 MOVSS XMM0, [RAX+9]
; CFA: 660F7EC2 MOVD EDX, XMM0
; CFE: 48C1E220 SHL RDX, 32
; D02: 80CA19 OR DL, 25
; D05: 488BE5 MOV RSP, RBP
; D08: F8 CLC
; D09: 5D POP RBP
; D0A: C3 RET
; D0B: CC10 INT3 16 ; Invalid argument count trap
; D0D: L2: 6A21 PUSH 33
; D0F: E801A616FF CALL #x52A00315 ; CONS->RNN
; D14: 58 POP RAX
; D15: E97BFFFFFF JMP L0
; D1A: L3: CC24 INT3 36 ; INVALID-VECTOR-INDEX-ERROR
; D1C: 00 BYTE #X00 ; RAX(d)
; D1D: 8A808010 BYTE #X8A, #X80, #X80, #X10 ; 2
Is this expected behavior?
Trying this library out for the first time, and cannot get this basic usage to compile on SBCL 2.1.7:
(in-package #:cl-user)
(defpackage #:origin2
(:use #:static-dispatch-cl))
(in-package #:origin2)
(defstruct (aggregate
(:predicate nil)
(:copier nil))
(storage (make-array 0 :element-type 'single-float :initial-element 0.0) :type (simple-array single-float (*))))
(defgeneric ref (sequence index))
(defmethod ref ((sequence aggregate) index)
(elt (aggregate-storage sequence) index))
(defgeneric (setf ref) (value sequence index))
(defmethod (setf ref) (value (sequence aggregate) index)
(setf (elt (aggregate-storage sequence) index) value))
(defun test ()
(declare (optimize (speed 3) (safety 2)))
(let ((a (make-aggregate :storage (make-array 3 :element-type 'single-float :initial-contents '(1.0 2.0 3.0)))))
(declare (type aggregate a))
(ref a 2)))
; file: /tmp/slimegXmN46
; in: DEFUN TEST
; (ORIGIN2::REF ORIGIN2::A 2)
;
; caught ERROR:
; don't know how to dump #<STANDARD-METHOD ORIGIN2::REF (AGGREGATE T) {1001A3DC23}> (default MAKE-LOAD-FORM method called).
;
; compilation unit finished
; caught 1 ERROR condition
Edit: Reproducible with latest Quicklisp dist version of static-dispatch as well as tip of master
Using the same boilerplate in #5 and changing test to:
(defun test ()
(declare (optimize (speed 3) (safety 2))
(static-dispatch-warn all))
(let ((a (make-aggregate :storage (make-array 3 :element-type 'single-float :initial-contents '(1.0 2.0 3.0)))))
(declare (type aggregate a))
(ref a 2)))
(added the static-dispatch-warn
declaration).
Fails to parse:
; file: /tmp/slimenOeOuC
; in: DEFUN TEST
; (ORIGIN2::REF ORIGIN2::A 2)
;
; caught STYLE-WARNING:
; Static dispatch for REF failed:
; Error while parsing arguments to DESTRUCTURING-BIND:
; invalid number of elements in
; (:DECLARE STATIC-DISPATCH-WARN . STATIC-DISPATCH::ALL)
; to satisfy lambda list
; (ENTRY-KEYWORD ENTRY-VAR ENTRY-BINDING &REST ENTRY-CONS):
; at least 3 expected, but got an improper list
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
I'm not able to compile static-dispatch
e.g. like this:
CL-USER> (asdf:oos 'asdf:compile-op :static-dispatch :force t)
due to this error:
; compiling file "/nix/store/ndsgsqm6gg0jcw4v4fbrlvkk8a9ax4r1-nixlisp-bundle-1.0.0/lib/common-lisp/bundle/software/static-dispatch-20210807-git/src/combinations.lisp" (written 01 JAN 1970 01:00:01 AM):
; processing (IN-PACKAGE #:STATIC-DISPATCH)
; processing (DEFINE-METHOD-COMBINATION% STANDARD ...)
[...]
The function
(COMMON-LISP:SETF STATIC-DISPATCH::COMBINATION-FUNCTION) is
undefined.
[Condition of type UNDEFINED-FUNCTION]
The problem seems to be that define-method-combination%
expands into code that is (eval-when (:compile-toplevel :load-toplevel :execute) ...)
and includes a call to (setf combination-function)
but that this is not defined at compile-time.
I'm not sure what the root issue is but it seems to be either too much or too little being declared to run at compile-time.
Hints would be appreciated as this is an obstacle to including static-dispatch
in my packaging framework of choice (Nix.)
method-descriptions are not honored for static-dispatch:defgeneric
forms, and dispatch falls back to dynamic mode.
It would be nice to support methods installed in this way, or extend the warning capability to inform the user of such a fallback.
find-method% in "src/common.lisp" is currently conditionalize such on clisp it constructs the specializers as it should, but on other implementation it doesn't.
On LispWorks, it should do the same as it does on clisp.
I actually found the issue when running tests for another system, "picl/tests", and just removing the conditinalization fixed it. It should work on all implementations, because that is what the standard says.
I am experiencing 3 different performance results consistently with the same code, depending on how I load it. Here is the test code:
(in-package #:cl-user)
(defpackage #:origin2
(:use #:static-dispatch-cl))
(in-package #:origin2)
(defstruct (aggregate
(:predicate nil)
(:copier nil))
(storage (make-array 0 :element-type 'single-float :initial-element 0.0) :type (simple-array single-float (*))))
(defgeneric ref (sequence index)
(:method ((sequence aggregate) index)
(elt (aggregate-storage sequence) index)))
(defun test ()
(declare (optimize (speed 3)))
(let ((a (make-aggregate :storage (make-array 3 :element-type 'single-float :initial-contents '(1.0 2.0 3.0)))))
(declare (type aggregate a))
(dotimes (i (expt 10 9))
(ref a 2))))
Clearing FASL's, loading this code into my image on SBCL 2.1.7, and issuing:
(progn
(sb-ext:gc :full t)
(time (test)))
I get these results consistently:
Evaluation took:
7.146 seconds of real time
7.130177 seconds of total run time (7.130177 user, 0.000000 system)
99.78% CPU
20 lambdas converted
14,232,702,075 processor cycles
1,080,336 bytes consed
If I now re-compile the #'test
function in the same image, and re-run the test I consistently get:
Evaluation took:
0.350 seconds of real time
0.351234 seconds of total run time (0.351196 user, 0.000038 system)
100.29% CPU
701,597,093 processor cycles
0 bytes consed
If I wrap the defgeneric
in (eval-when (:compile-toplevel :load-toplevel :execute) ...)
, clear FASL's, loading the code, and re-run the test, I consistently get:
Evaluation took:
0.583 seconds of real time
0.579642 seconds of total run time (0.579642 user, 0.000000 system)
99.49% CPU
1,157,429,524 processor cycles
0 bytes consed
I get no warnings from static-dispatch in all 3 cases, and everything compiles cleanly. I would like to know why these differences are occurring, and how I would get the results of the second test above from a clean image (instead of manually recompiling the function after loading). Thank you for your help.
Filename AUX (whatever case, whatever extension) is special on Windows (legacy of DOS).
./test/aux.lisp cannot be written nor read on windows, and ql:quickload fails because of it :-(
I wrote a small demo:
(ql:quickload 'static-dispatch)
(declaim (inline sum))
(defgeneric sum (a b)
(:documentation "sum two values"))
(static-dispatch:defmethod sum ((a fixnum) (b fixnum))
(declare (optimize (speed 3) (safety 0) (debug 0)))
(+ a b))
(defun test-sum-inline (a b)
(declare (optimize (speed 3) (safety 0) (debug 0))
(type fixnum a b))
(sum a b))
but SBCL v1.3.21 with errors occur when compile function test-sum-inline
:
; in: DEFUN TEST-SUM-INLINE
; (SUM A B)
;
; caught WARNING:
; Error during compiler-macroexpansion of (SUM A B). Use *BREAK-ON-SIGNALS* to
; intercept.
;
; invalid number of arguments: 1
;
; compilation unit finished
; caught 1 WARNING condition
Did I use static-dispatch in the wrong way? Could you please give me a working demo?
Building with SBCL 2.1.10 / ASDF 3.3.5 for quicklisp dist creation.
Trying to build commit id 8dd50fa
static-dispatch/test fails to build with the following error:
; caught WARNING:
; * is not permitted as an argument to the FUNCTION type specifier
;
; caught WARNING:
; * is not permitted as an argument to the FUNCTION type specifier
...
Unhandled UIOP/LISP-BUILD:COMPILE-FILE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING {1001C08103}>: COMPILE-FILE-ERROR while compiling #<CL-SOURCE-FILE "static-dispatch/test" "test" "internals">
Consider the following generic function intended to access an element from a matrix of column vectors. For the 2x1 matrix type, aka mathematical vector, the column
parameter is declared ignored.
(defgeneric ref (aggregate row &optional column))
(defmethod ref ((aggregate vec2) row &optional column)
(declare (ignore column))
(locally (declare (optimize (safety 0)))
(aref (storage aggregate) index)))
;; in a later compilation unit
(declaim (ftype (function (vec2 (integer 0 2)) single-float) test))
(defun test (vec2 index)
(declare (optimize speed))
(ref vec2 index))
However:
; in: DEFUN TEST
; (ORIGIN2::REF ORIGIN2::VEC2 ORIGIN2::INDEX)
;
; caught STYLE-WARNING:
; IGNORE declaration for a variable from outer scope: COLUMN
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.