Fork me on GitHub

^ working prototype of making maps get emitted with an array templating scheme, using invokedynamic. I should draw a picture to explain how it works


I described it in this room earlier this week


Given this source code

(defn testmap
  [a b c]
  {:foo a
   :bar b
   :quuz c})


The currently emitted bytecode to create the map inside the body:

;; NB there are details elided
;;   locals clearing
;;   dup'ing the array to keep it on the stack
;;   pushing the integers for array access

;; mkarray size=6
;; push :foo
;; set array slot 0
;; push a
;; set array slot 1
;; push :bar
;; set array slot 2
;; push b
;; set array slot 3
;; push :quuz
;; set array slot 4
;; push b
;; set array slot 5
;; call PHM.create(array)


(the elided details make the bytecode a lot longer)


the experimental bytecode

;; push a
;; push b
;; push c
;; indy createMapUsingTemplate

;; indy BootStrapMethod createMapUsingTemplate (N.B. executed exactly once; static helper)
;; let template = object array[:foo nil :bar :nil :quuz nil]
;; return CallSite(MethodHandle.partial(RT.templatedMapBuilder, template, [1, 3, 5]))

;; RT.templatedMapBuilder template, slots, args (static helper)
;;  copy template
;;   for [i, arg] in [slots, args]
;;     set template i arg
;;  PHM.create(template)


The idea here is to pre-fill an array "template" with the static parts of the map (currently limited to keywords). To create the map, you push all the dynamic parts of the map onto the stack. The method handle executed knows which slots on the template it needs to fill in with the dynamic args.


(internally, it uses an int bitmap to mark which slots of the array template are dynamic vs static)


The same scheme could be done for vector expressions.


@ghadi - Nice. Any performance measurements, e.g. even just compiling Clojure itself and running its built-in tests?


Not yet. I'm trying some measurements against a simple criterium thing, then the clojurescript compiler (stuff like the analyzer should benefit). There will not be any benefits to something like compiling clojure itself. The MethodHandles have to get hot. I managed to do it without any regression to startup time, which is +1. Just loading java.lang.invoke.* adds a lot of classes to your JVM.