Changeset 12042


Ignore:
Timestamp:
07/13/09 14:10:50 (14 years ago)
Author:
Mark Evenson
Message:

Cache arg specialization computation for eql-specialized generic functions. (Anton Vodonosov)

I started to investigate this problem because slime fuzzy completion
works very slow with ABCL.
For example

(time (swank::fuzzy-completions "de" 'cl-user))

takes 1.5 - 2 seconds. That long time is not pleasant for user interface.

Turned out that most of the time is spent in COMPILE.

During SWANK::FUZZY-COMPLETIONS every symbol is processed by
SWANK-CLASSIFY-SYMBOL, which uses (DOCUMENTATION s 'TYPE),
and every invocation (DOCUMENTATION s 'TYPE) being eql-specialized
function leads to COMPILE.

With my patch (time (swank::fuzzy-completions "de" 'cl-user))
takes 0.25 - 0.5 seconds. Not too fast, in CLISP the same takes 0.07 sec,
in CCL 0.03 sec. But significantly better than it was.

In the patch method getArgSpecialization could probably be named better.
Also I am not sure if you want to keep the wordy javadoc comment; i just
decided to put the patch explanation to the sources instead of this email.

Location:
trunk/abcl/src/org/armedbear/lisp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/StandardGenericFunction.java

    r11754 r12042  
    446446        throws ConditionThrowable
    447447      {
    448           checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_METHOD_COMBINATION] = second;
     448          checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_METHOD_COMBINATION]
     449      = second;
    449450          return second;
    450451      }
     
    458459      public LispObject execute(LispObject arg) throws ConditionThrowable
    459460      {
    460           return checkStandardGenericFunction(arg).slots[StandardGenericFunctionClass.SLOT_INDEX_ARGUMENT_PRECEDENCE_ORDER];
     461          return checkStandardGenericFunction(arg).slots[StandardGenericFunctionClass
     462               .SLOT_INDEX_ARGUMENT_PRECEDENCE_ORDER];
    461463      }
    462464    };
     
    470472        throws ConditionThrowable
    471473      {
    472           checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_ARGUMENT_PRECEDENCE_ORDER] = second;
     474          checkStandardGenericFunction(first)
     475      .slots[StandardGenericFunctionClass.SLOT_INDEX_ARGUMENT_PRECEDENCE_ORDER] = second;
    473476          return second;
    474477      }
     
    482485      public LispObject execute(LispObject arg) throws ConditionThrowable
    483486      {
    484           return checkStandardGenericFunction(arg).slots[StandardGenericFunctionClass.SLOT_INDEX_CLASSES_TO_EMF_TABLE];
     487          return checkStandardGenericFunction(arg)
     488      .slots[StandardGenericFunctionClass.SLOT_INDEX_CLASSES_TO_EMF_TABLE];
    485489      }
    486490    };
     
    494498        throws ConditionThrowable
    495499      {
    496           checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_CLASSES_TO_EMF_TABLE] = second;
     500          checkStandardGenericFunction(first)
     501      .slots[StandardGenericFunctionClass.SLOT_INDEX_CLASSES_TO_EMF_TABLE] = second;
    497502          return second;
    498503      }
     
    518523        throws ConditionThrowable
    519524      {
    520           checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_DOCUMENTATION] = second;
     525          checkStandardGenericFunction(first).slots[StandardGenericFunctionClass.SLOT_INDEX_DOCUMENTATION]
     526      = second;
    521527          return second;
    522528      }
     
    551557        for (int i = gf.numberOfRequiredArgs; i-- > 0;)
    552558          {
    553             array[i] = args.car().classOf();
     559            array[i] = gf.getArgSpecialization(args.car());
    554560            args = args.cdr();
    555561          }
    556         CacheEntry classes = new CacheEntry(array);
     562        CacheEntry specializations = new CacheEntry(array);
    557563        HashMap<CacheEntry,LispObject> ht = gf.cache;
    558564        if (ht == null)
    559565            ht = gf.cache = new HashMap<CacheEntry,LispObject>();
    560         ht.put(classes, third);
     566        ht.put(specializations, third);
    561567        return third;
    562568      }
     
    576582        for (int i = gf.numberOfRequiredArgs; i-- > 0;)
    577583          {
    578             array[i] = args.car().classOf();
     584            array[i] = gf.getArgSpecialization(args.car());
    579585            args = args.cdr();
    580586          }
    581         CacheEntry classes = new CacheEntry(array);
     587        CacheEntry specializations = new CacheEntry(array);
    582588        HashMap<CacheEntry,LispObject> ht = gf.cache;
    583589        if (ht == null)
    584590          return NIL;
    585         LispObject emf = (LispObject) ht.get(classes);
     591        LispObject emf = (LispObject) ht.get(specializations);
    586592        return emf != null ? emf : NIL;
     593      }
     594    };
     595
     596  /**
     597   * Returns an object representing generic function
     598   * argument <tt>arg</tt> in a <tt>CacheEntry</tt>
     599   *
     600   * <p>In the simplest case, when this generic function
     601   * does not have EQL specialized methos, and therefore
     602   * only argument types are relevant for choosing
     603   * applicable methods, the value returned is the
     604   * class of <tt>arg</tt>
     605   *
     606   * <p>If the function has EQL specialized methods:
     607   *   - if <tt>arg</tt> is EQL to some of the EQL-specializers,
     608   *     a special object representing equality to that specializer
     609   *     is returned.
     610   *   - otherwise class of the <tt>arg</tt> is returned.
     611   *
     612   * <p>Note that we do not consider argument position, when
     613   * calculating arg specialization. In rare cases (when
     614   * one argument is eql-specialized to a symbol specifying
     615   * class of another argument) this may result in redundant cache
     616   * entries caching the same method. But the method cached is anyway
     617   * correct for the arguments (because in case of cache miss, correct method
     618   * is calculated by other code, which does not rely on getArgSpecialization;
     619   * and because EQL is true only for objects of the same type, which guaranties
     620   * that if a type-specialized methods was chached by eql-specialization,
     621   * all the cache hits into this records will be from args of the conforming
     622   * type).
     623   *
     624   * <p>Consider:
     625   * <pre><tt>
     626   * (defgeneric f (a b))
     627   *
     628   * (defmethod f (a (b (eql 'symbol)))
     629   *   "T (EQL 'SYMBOL)")
     630   *
     631   * (defmethod f ((a symbol) (b (eql 'symbol)))
     632   *   "SYMBOL (EQL 'SYMBOL)")
     633   *
     634   * (f 12 'symbol)
     635   * => "T (EQL 'SYMBOL)"
     636   *
     637   * (f 'twelve 'symbol)
     638   * => "SYMBOL (EQL 'SYMBOL)"
     639   *
     640   * (f 'symbol 'symbol)
     641   * => "SYMBOL (EQL 'SYMBOL)"
     642   *
     643   * </tt></pre>
     644   *
     645   * After the two above calls <tt>cache</tt> will contain tree keys:
     646   * <pre>
     647   * { class FIXNUM, EqlSpecialization('SYMBOL) }
     648   * { class SYMBOL, EqlSpecialization('SYMBOL) }
     649   * { EqlSpecialization('SYMBOL), EqlSpecialization('SYMBOL) }.
     650   * </pre>
     651   */     
     652  private LispObject getArgSpecialization(LispObject arg)
     653  {
     654    for (EqlSpecialization eqlSpecialization : eqlSpecializations)
     655      {
     656        if (eqlSpecialization.eqlTo.eql(arg))
     657          return eqlSpecialization;
     658      }
     659    return arg.classOf();
     660  }
     661
     662  // ### %get-arg-specialization
     663  private static final Primitive _GET_ARG_SPECIALIZATION =
     664    new Primitive("%get-arg-specialization", PACKAGE_SYS, true, "generic-function arg")
     665    {
     666      @Override
     667      public LispObject execute(LispObject first, LispObject second)
     668        throws ConditionThrowable
     669      {
     670        final StandardGenericFunction gf = checkStandardGenericFunction(first);
     671        return gf.getArgSpecialization(second);
    587672      }
    588673    };
     
    667752    }
    668753  }
     754
     755  private EqlSpecialization eqlSpecializations[] = new EqlSpecialization[0];
     756
     757    // ### %init-eql-specializations
     758    private static final Primitive _INIT_EQL_SPECIALIZATIONS
     759      = new Primitive("%init-eql-specializations", PACKAGE_SYS, true,
     760        "generic-function eql-specilizer-objects-list")
     761      {
     762        @Override
     763        public LispObject execute(LispObject first, LispObject second)
     764          throws ConditionThrowable
     765        {
     766          final StandardGenericFunction gf = checkStandardGenericFunction(first);
     767          LispObject eqlSpecializerObjects = second;
     768          gf.eqlSpecializations = new EqlSpecialization[eqlSpecializerObjects.length()];
     769          for (int i = 0; i < gf.eqlSpecializations.length; i++) {
     770      gf.eqlSpecializations[i] = new EqlSpecialization(eqlSpecializerObjects.car());
     771      eqlSpecializerObjects = eqlSpecializerObjects.cdr();
     772          }
     773          return NIL;
     774        }
     775      };
     776
     777  private static class EqlSpecialization extends LispObject
     778  {
     779    public LispObject eqlTo;
     780
     781    public EqlSpecialization(LispObject eqlTo)
     782    {
     783        this.eqlTo = eqlTo;
     784    }
     785  }
    669786 
    670787  public static final StandardGenericFunction checkStandardGenericFunction(LispObject obj)
    671788  throws ConditionThrowable
    672789  {
    673                 if (obj instanceof StandardGenericFunction)
    674                         return (StandardGenericFunction) obj;
    675                 return (StandardGenericFunction) // Not reached.
    676                 type_error(obj, Symbol.STANDARD_GENERIC_FUNCTION);
    677         }
     790    if (obj instanceof StandardGenericFunction)
     791      return (StandardGenericFunction) obj;
     792    return (StandardGenericFunction) // Not reached.
     793      type_error(obj, Symbol.STANDARD_GENERIC_FUNCTION);
     794  }
    678795}
  • trunk/abcl/src/org/armedbear/lisp/clos.lisp

    r11992 r12042  
    858858  (apply gf args))
    859859
     860(defun collect-eql-specializer-objects (generic-function)
     861  (let ((result nil))
     862    (dolist (method (generic-function-methods generic-function))
     863      (dolist (specializer (%method-specializers method))
     864        (when (typep specializer 'eql-specializer)
     865          (pushnew (eql-specializer-object specializer)
     866                   result
     867                   :test 'eql))))
     868    result))
     869
    860870(defun finalize-generic-function (gf)
    861871  (%finalize-generic-function gf)
    862872  (setf (classes-to-emf-table gf) (make-hash-table :test #'equal))
     873  (%init-eql-specializations gf (collect-eql-specializer-objects gf))
    863874  (set-funcallable-instance-function
    864875   gf
     
    11851196        method)))
    11861197
    1187 (defun methods-contain-eql-specializer-p (methods)
    1188   (dolist (method methods nil)
    1189     (when (dolist (spec (%method-specializers method) nil)
    1190             (when (eql-specializer-p spec) (return t)))
    1191       (return t))))
    1192 
    11931198(defun fast-callable-p (gf)
    11941199  (and (eq (generic-function-method-combination gf) 'standard)
     
    12061211(defun std-compute-discriminating-function (gf)
    12071212  (let ((code
    1208          (cond ((methods-contain-eql-specializer-p (generic-function-methods gf))
    1209                 (make-closure `(lambda (&rest args)
    1210                                  (slow-method-lookup ,gf args))
    1211                               nil))
    1212                ((and (= (length (generic-function-methods gf)) 1)
     1213         (cond ((and (= (length (generic-function-methods gf)) 1)
    12131214                     (typep (car (generic-function-methods gf)) 'standard-reader-method))
    12141215;;                 (sys::%format t "standard reader function ~S~%" (generic-function-name gf))
     
    12461247                                          (= (length (generic-function-methods gf)) 1))
    12471248                                     (let* ((method (%car (generic-function-methods gf)))
    1248                                             (class (car (%method-specializers method)))
     1249                                            (specializer (car (%method-specializers method)))
    12491250                                            (function (or (%method-fast-function method)
    12501251                                                          (%method-function method))))
    1251                                        `(lambda (arg)
    1252                                           (declare (optimize speed))
    1253                                           (unless (simple-typep arg ,class)
    1254                                             ;; FIXME no applicable method
    1255                                             (error 'simple-type-error
    1256                                                    :datum arg
    1257                                                    :expected-type ,class))
    1258                                           (funcall ,function arg))))
     1252                                       (if (eql-specializer-p specializer)
     1253                                           (let ((specializer-object (eql-specializer-object specializer)))
     1254                                             `(lambda (arg)
     1255                                                (declare (optimize speed))
     1256                                                (if (eql arg ',specializer-object)
     1257                                                    (funcall ,function arg)
     1258                                                    (no-applicable-method ,gf (list arg)))))
     1259                                           `(lambda (arg)
     1260                                              (declare (optimize speed))
     1261                                              (unless (simple-typep arg ,specializer)
     1262                                                ;; FIXME no applicable method
     1263                                                (error 'simple-type-error
     1264                                                       :datum arg
     1265                                                       :expected-type ,specializer))
     1266                                              (funcall ,function arg)))))
    12591267                                    (t
    12601268                                     `(lambda (arg)
    12611269                                        (declare (optimize speed))
    1262                                         (let* ((class (class-of arg))
    1263                                                (emfun (or (gethash1 class ,emf-table)
    1264                                                           (slow-method-lookup-1 ,gf class))))
     1270                                        (let* ((specialization (%get-arg-specialization ,gf arg))
     1271                                               (emfun (or (gethash1 specialization ,emf-table)
     1272                                                          (slow-method-lookup-1 ,gf arg specialization))))
    12651273                                          (if emfun
    12661274                                              (funcall emfun (list arg))
     
    12761284                                   (if emfun
    12771285                                       (funcall emfun args)
    1278                                        (slow-method-lookup ,gf args))))))
     1286                                      (slow-method-lookup ,gf args))))))
    12791287                         ((= number-required 2)
    12801288                          (if exact
     
    13691377        (return nil)))))
    13701378
    1371 (defun %compute-applicable-methods-using-classes (gf required-classes)
    1372   (let ((methods '()))
    1373     (dolist (method (generic-function-methods gf))
    1374       (when (method-applicable-p-using-classes method required-classes)
    1375         (push method methods)))
    1376     (if (or (null methods) (null (%cdr methods)))
    1377         methods
    1378         (sort methods
    1379               (if (eq (class-of gf) (find-class 'standard-generic-function))
    1380                   #'(lambda (m1 m2)
    1381                      (std-method-more-specific-p m1 m2 required-classes
    1382                                                  (generic-function-argument-precedence-order gf)))
    1383                   #'(lambda (m1 m2)
    1384                      (method-more-specific-p gf m1 m2 required-classes)))))))
    1385 
    13861379(defun slow-method-lookup (gf args)
    13871380  (let ((applicable-methods (%compute-applicable-methods gf args)))
     
    13951388        (apply #'no-applicable-method gf args))))
    13961389
    1397 (defun slow-method-lookup-1 (gf class)
    1398   (let ((applicable-methods (%compute-applicable-methods-using-classes gf (list class))))
     1390(defun slow-method-lookup-1 (gf arg arg-specialization)
     1391  (let ((applicable-methods (%compute-applicable-methods gf (list arg))))
    13991392    (if applicable-methods
    14001393        (let ((emfun (funcall (if (eq (class-of gf) (find-class 'standard-generic-function))
     
    14031396                              gf applicable-methods)))
    14041397          (when emfun
    1405             (setf (gethash class (classes-to-emf-table gf)) emfun))
     1398            (setf (gethash arg-specialization (classes-to-emf-table gf)) emfun))
    14061399          emfun))))
    14071400
Note: See TracChangeset for help on using the changeset viewer.