Ticket #420: beta-reduce-20161117a.diff

File beta-reduce-20161117a.diff, 10.1 KB (added by Mark Evenson, 7 years ago)

evenson-testing

  • contrib/jss/invoke.lisp

    # HG changeset patch
    # User Alan Ruttenberg <alanruttenberg@gmail.com>
    # Date 1477731360 14400
    #      Sat Oct 29 04:56:00 2016 -0400
    # Node ID 620a834d38743096b6c64e39c318578fd8657d23
    # Parent  ad2cf4edd552c42160371182af5f05843fa39d59
    precompiler: possibly optimize lambda in function position (Alan Ruttenberg)
    
    ---
    Notes from testing on 17-NOV-2016
    
    The current patch has the following additional ANSI-TEST failures:
    
    LAMBDA.1, LAMBDA.2, LAMBDA.3, LAMBDA.4, LAMBDA.5, LAMBDA.6, LAMBDA.7,
    LAMBDA.8, LAMBDA.9, LAMBDA.10, LAMBDA.21, LAMBDA.22, LAMBDA.54,
    LAMBDA.57, LAMBDA.63, LAMBDA.64
    
    The ANSI tests require that the [ansi-tests][1] be manually installed
    as a sibling directory of the ABCL build directory named 'ansi-test'.
    
    [1]: git+https://gitlab.common-lisp.net/ansi-test/ansi-test.git
    
    Then one may invoke the ANSI-TESTS via
    
        CL-USER> (asdf:load-system :abcl)(asdf:test-system :ansi-compiled)
    
    Unfortunately the optimization for JSS requires that all necessary
    Java objects have resolvable linkage at compile time, something that
    wasn't required before.
    
    The optimization for JSS is controlled by the
    JSS:*INHIBIT-JSS-OPTIMIZATION* boolean, which is now true by default.
    To try out this optimization
    
        (setf JSS:*INHIBIT-JSS-OPTIMIZATION* t)
    
    ---
    
    Precompiler: When compiling a form with a lambda in the function
    position, possibly optimize it
    
    Case 1: If the lambda has a single form in it, let someone define a
    transform using:
    
    define-function-position-lambda-transform (body-function-name (arglist form args) &body body)
    
    body-function-name is the car of the single form in the lambda
    arglist is the arglist of the lambda
    form is the single form within the lambda
    args are the arguments to which the lambda will be defined.
    
    The function should check whether it can do a transform, and do it if
    so, otherwise return nil signalling it couldn't
    
    Case 2: If case 1 is not successful then if the arglist is a simple
    one (no &key, &rest, &optional) then do a standard beta-reduction
    binding the args to arglist using let (https://wiki.haskell.org/Beta_reduction)
    
    If not, return and do the usual thing.
    
    An example is in contrib/jss/optimize-java-call.lisp
    
    To see benefits, (compile-file contrib/jss/test-optimize-java-call.lisp)
    and then load the compiled file. You should see something like the below
    which reports the timings for the optimized and unoptimized version of
    10000 calls of (#"compile" 'regex.pattern ".*")
    
    --
    
    With optimization: (INVOKE-RESTARGS-MACRO "compile" (QUOTE REGEX.PATTERN) (LIST ".*") NIL T)
    Without optimization: ((LAMBDA (#:G85648 &REST #:G85649) (INVOKE-RESTARGS "compile" #:G85648 #:G85649 NIL)) (QUOTE REGEX.PATTERN) ".*")
    
    JUST-LOOP
    0.0 seconds real time
    0 cons cells
    
    OPTIMIZED-JSS
    0.011 seconds real time
    0 cons cells
    
    UNOPTIMIZED-JSS
    0.325 seconds real time
    800156 cons cells
    
    See:
    https://mailman.common-lisp.net/pipermail/armedbear-devel/2016-October/003726.html
    https://mailman.common-lisp.net/pipermail/armedbear-devel/2016-November/003733.html
    
    diff -r ad2cf4edd552 contrib/jss/invoke.lisp
    a b  
    288288                              (group "group"))
    289289      (loop while (hasmore entries)
    290290         for name =  (getname (next entries))
    291          with class-pattern = (#"compile" '|java.util.regex.Pattern| ".*\\.class$")
    292          with name-pattern = (#"compile" '|java.util.regex.Pattern| ".*?([^.]*)$")
     291         with class-pattern = (jstatic "compile" "java.util.regex.Pattern" ".*\\.class$")
     292         with name-pattern = (jstatic "compile" "java.util.regex.Pattern" ".*?([^.]*)$")
    293293         when (matches (matcher class-pattern name))
    294294         collect
    295295           (let* ((fullname (substring (jreplace name #\/ #\.) 0 (- (jlength name) 6)))
  • contrib/jss/jss.asd

    diff -r ad2cf4edd552 contrib/jss/jss.asd
    a b  
    11;;;; -*- Mode: LISP -*-
    22(asdf:defsystem :jss
    33  :author "Alan Ruttenberg, Mark Evenson"
    4   :version "3.1.1"
    5   :description "<> asdf:defsystem <urn:abcl.org/release/1.5.0/contrib/jss#3.1.1>"
     4  :version "3.2.0"
     5  :description "<> asdf:defsystem <urn:abcl.org/release/1.5.0/contrib/jss#3.2.0>"
    66  :components ((:module base
    77                        :pathname "" :serial t
    88                        :components ((:file "packages")
  • new file contrib/jss/optimize-java-call.lisp

    diff -r ad2cf4edd552 contrib/jss/optimize-java-call.lisp
    - +  
     1(in-package :jss)
     2
     3(defvar *inhibit-jss-optimization* t)
     4
     5;; https://mailman.common-lisp.net/pipermail/armedbear-devel/2016-October/003726.html
     6
     7(precompiler::define-function-position-lambda-transform jss::invoke-restargs (arglist form args)
     8  (declare (ignore arglist))
     9  (unless *inhibit-jss-optimization*
     10    (precompiler::precompile-function-call
     11     `(jss::invoke-restargs-macro
     12    ,(second form)
     13    ,(car args) (list ,@(cdr args)) ,(fifth form)))))
     14
     15(defmacro invoke-restargs-macro ( method object args &optional (raw? nil))
     16  (assert (eq (car args) 'list))
     17  (setq args (cdr args))
     18  (if (and (consp object) (eq (car object) 'quote))
     19      (let ((object (eval object)))
     20  (let* ((object-as-class-name
     21     (if (symbolp object)
     22         (jss::maybe-resolve-class-against-imports object)
     23         ))
     24         (object-as-class
     25     (if object-as-class-name (find-java-class object-as-class-name))))
     26    (if raw?
     27        `(jstatic-raw ,method ,object-as-class ,@args)
     28        `(jstatic ,method ,object-as-class ,@args))))
     29      (if raw?
     30    `(if (symbolp ,object)
     31         (jstatic-raw ,method (find-java-class ,object) ,@args)
     32         (jcall-raw ,method ,object ,@args))
     33    `(if (symbolp ,object)
     34         (jstatic ,method (find-java-class ,object) ,@args)
     35         (jcall ,method ,object ,@args)))))
     36
  • contrib/jss/packages.lisp

    diff -r ad2cf4edd552 contrib/jss/packages.lisp
    a b  
    33  (:use :common-lisp :extensions :java)
    44  (:export
    55   #:*inhibit-add-to-classpath*
     6   #:*inhibit-jss-optimization*
    67   #:*added-to-classpath*
    78   #:*do-auto-imports*
    89   #:*muffle-warnings*
  • new file contrib/jss/test-optimize-java-call.lisp

    diff -r ad2cf4edd552 contrib/jss/test-optimize-java-call.lisp
    - +  
     1(in-package :jss)
     2
     3(format t "~%~%")
     4
     5(eval-when (:compile-toplevel :load-toplevel)
     6  (setq *inhibit-jss-optimization* nil)
     7  (format t "With optimization: ~s~%" (macroexpand (precompiler::precompile-form '(#"compile" 'regex.Pattern ".*") t))))
     8
     9(defun optimized-jss (count)
     10  (loop repeat count do (#"compile" 'regex.Pattern ".*")))
     11
     12(eval-when (:compile-toplevel :load-toplevel)
     13  (setq *inhibit-jss-optimization* t)
     14  (format t "Without optimization: ~s~%" (precompiler::precompile-form '(#"compile" 'regex.Pattern ".*") t)))
     15
     16(defun unoptimized-jss (count)
     17  (loop repeat count do (#"compile" 'regex.Pattern ".*")))
     18
     19(defun just-loop (count)
     20  (loop repeat count))
     21
     22(print 'just-loop)
     23(time (just-loop 10000))
     24(print 'first)
     25(time (optimized-jss 10000))
     26(print 'second)
     27(time (unoptimized-jss 10000))
     28
     29#|
     30With optimization: (INVOKE-RESTARGS-MACRO "compile" (QUOTE REGEX.PATTERN) (LIST ".*") NIL T)
     31Without optimization: ((LAMBDA (#:G85648 &REST #:G85649) (INVOKE-RESTARGS "compile" #:G85648 #:G85649 NIL)) (QUOTE REGEX.PATTERN) ".*")
     32
     33JUST-LOOP
     340.0 seconds real time
     350 cons cells
     36
     37OPTIMIZED-JSS
     380.011 seconds real time
     390 cons cells
     40
     41UNOPTIMIZED-JSS
     420.325 seconds real time
     43800156 cons cells
     44|#
  • src/org/armedbear/lisp/precompiler.lisp

    diff -r ad2cf4edd552 src/org/armedbear/lisp/precompiler.lisp
    a b  
    382382  (let ((op (car form)))
    383383    (when (and (consp op) (eq (%car op) 'LAMBDA))
    384384      (return-from precompile-function-call
    385                    (cons (precompile-lambda op)
    386                          (mapcar #'precompile1 (cdr form)))))
     385  (or (precompile-function-position-lambda op (cdr form))
     386      (cons (precompile-lambda op)
     387      (mapcar #'precompile1 (cdr form))))))
    387388    (when (or (not *in-jvm-compile*) (notinline-p op))
    388389      (return-from precompile-function-call (precompile-cons form)))
    389390    (when (source-transform op)
     
    399400          (return-from precompile-function-call (precompile1 (expand-inline form expansion))))))
    400401    (cons op (mapcar #'precompile1 (cdr form)))))
    401402
     403(defun precompile-function-position-lambda (lambda args)
     404  (let* ((arglist (second lambda))
     405   (body (cddr lambda))
     406   (simple-arglist? (not (or (memq '&KEY arglist) (memq '&OPTIONAL arglist) (memq '&REST arglist)))))
     407    (or
     408     ;;give a chance for someone to transform single-form function bodies
     409     (and (= (length body) 1) (consp (car body)) (get (caar body) 'sys::function-position-lambda-transform)
     410    (funcall (get (caar body) 'sys::function-position-lambda-transform) (caar body) (car body) (mapcar #'precompile1 args)))
     411     (and simple-arglist?
     412    (let ((arglist-length (if (memq '&aux arglist) (position '&aux arglist) (length arglist))))
     413      (if (= (length args) arglist-length)
     414    ;; simplest case - we have a simple arglist with as many
     415    ;; arguments as call args. Transform to let.
     416    (return-from precompile-function-position-lambda
     417      `((let* ,(append
     418          (loop for arg-name in arglist
     419          for arg in (mapcar #'precompile1 args)
     420          until (eq arg-name '&aux)
     421          collect (list arg-name arg))
     422          (subseq arglist (1+ arglist-length)))
     423          ,@body)))
     424    (error "Argument mismatch for lambda in function position: ~a applied to ~a" `(lambda ,arglist body) args)))))))
     425
     426(defmacro define-function-position-lambda-transform (body-function-name (arglist form args) &body body)
     427  `(put ',body-function-name 'sys::function-position-lambda-transform
     428  #'(lambda(,arglist ,form ,args)
     429      ,@body)))
     430
    402431(defun precompile-locally (form)
    403432  (let ((*inline-declarations* *inline-declarations*))
    404433    (process-optimization-declarations (cdr form))