[HN Gopher] Common Lisp code optimisation
       ___________________________________________________________________
        
       Common Lisp code optimisation
        
       Author : lokedhs
       Score  : 85 points
       Date   : 2021-11-07 16:00 UTC (7 hours ago)
        
 (HTM) web link (write.as)
 (TXT) w3m dump (write.as)
        
       | bluefox wrote:
       | If you want to declare that the function takes a fixnum and
       | returns a fixnum, you can use a function type declaration:
       | (declaim (ftype (function (fixnum) (values fixnum &optional))
       | foo))
       | 
       | Of course, if you pass a fixnum and the result cannot actually be
       | stored in a fixnum, that's no good. So you need to either handle
       | that case or not make such a declaration.
        
         | lispm wrote:
         | for a specific expression on can also specify the return type
         | using THE:                   (the fixnum (+ a b))
        
           | bluefox wrote:
           | Right, though I'm not sure it's easy to infer an ordinary
           | function's return type from this kind of declaration.
           | 
           | Speaking of return types, do you have any idea if any
           | implementation takes a generic function's return type
           | declaration seriously? I believe SBCL currently doesn't,
           | which is unfortunate (defmethods keep clobbering the ftype
           | declaration).
        
           | twoodfin wrote:
           | That's a hilarious but perfect piece of syntax. Do you know
           | where it originated?
        
             | Jtsummers wrote:
             | Per:
             | https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node106.html
             | 
             | > Compatibility note: This construct is borrowed from the
             | Interlisp DECL package; Interlisp, however, allows an
             | implicit progn after the type specifier rather than just a
             | single form. The MacLisp fixnum-identity and flonum-
             | identity constructs can be expressed as (the fixnum x) and
             | (the single-float x).
        
             | vindarel wrote:
             | agreed^^ there are macros and libraries to bring a nicer
             | syntax (of course). Exple: https://github.com/lisp-
             | maintainers/defstar                  (defun* (sum -> real)
             | ((a real) (b real))            (+ a b))
        
       | podiki wrote:
       | Should have a (2020) tag, though Common Lisp has a standard,
       | these optimizations are done by specific implementations
       | (compilers).
        
         | lokedhs wrote:
         | True, although this particular simple optimisation is done by
         | most Common Lisp compilers.
        
       | samsaga2 wrote:
       | The problem with that type of optimizations is that is too
       | verbose to be useful. You have to type boring extra code.
        
         | jlarocco wrote:
         | I don't really agree with that. It's hardly "too verbose to be
         | useful" when a handful of type declarations can result in a
         | significant runtime speed up.
         | 
         | In practice only a relatively small amount of code will need
         | type declarations and optimizations enabled, and type
         | declarations double as safety checks when using "(declaim
         | (optimize (speed 0) (safety 3)))" instead of "(declaim
         | (optimize (speed 3) (safety 0)))", so it's a win for more than
         | just performance reasons.
         | 
         | It's not an optimization panacea, but it's low hanging fruit to
         | get started.
        
         | remexre wrote:
         | Also, the "hot loop hypothesis" still tends to hold in CL; you
         | only end up needing to annotate the crap out of the small
         | number of functions that end up being most of your runtime. (It
         | can sometimes be worth it to declare types for other functions,
         | but that's as much for error checking as performance.)
         | 
         | Being able to set the compiler flags on a per-function level is
         | also really helpful for this, since these hot functions can be
         | compiled for run-time speed rather than safety, debuggability,
         | or compile-time speed (once one's sure they're correct, of
         | course!).
        
           | guenthert wrote:
           | > It can sometimes be worth it to declare types for other
           | functions, but that's as much for error checking as
           | performance.
           | 
           | And readability!
        
         | mpfundstein wrote:
         | i maintain two big projects. i only ever annotate code after i
         | know i am really done with it. and also not every code. its a
         | dynamic language after all. its quite nice though that I can do
         | it when I want and omit when not
        
           | lispm wrote:
           | The big difference between SBCL (and related implementations
           | like CMUCL) compared to other Common Lisp implementations is
           | that SBCL uses type declarations and type inference for
           | compile time checks -> thus it can be less dynamic.
        
         | romero-jk wrote:
         | That's a big problem in clojure too, were you have to write
         | lots of javaish clojure to get decent performance.
        
         | lokedhs wrote:
         | Modern compilers, and in particular SBCL is able to use type
         | inference so that in many cases it is able to deduce these
         | optimisations even without explicit type specifications. Or at
         | least you can add the type specification in one place and then
         | other functions will be able to take advantage of this.
         | 
         | I should probably write a followup article that goes into more
         | details on this.
        
           | dunefox wrote:
           | Please do!
        
           | spicybright wrote:
           | Also, I'd imagine you can get a lot of mileage with macros
           | here, no?
        
           | User23 wrote:
           | I've had no trouble getting SBCL to infer return types when
           | calling other functions with the appropriate DECLARE or
           | DECLAIM (as displayed with DISASSEMBLE and DESCRIBE), but so
           | far I've not been able to verify that the compiler will infer
           | the argument types in the same case. For example, if F takes
           | two (signed-byte 8) and returns a (signed-byte 8) and I
           | (defun g (a b) (f a b)) DESCRIBE will say that G takes two
           | arguments of type T and returns a (signed-byte 8).
           | 
           | Is this something you can please shed some light on? Without
           | argument type inference it's hard to imagine how efficient
           | code is going to be reliably generated without explicit
           | declarations.
        
       ___________________________________________________________________
       (page generated 2021-11-07 23:01 UTC)