Implement memoization of upcall and downcall classes
This commit is contained in:
parent
bd7216a06e
commit
cce6c823f6
2 changed files with 120 additions and 81 deletions
|
|
@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. This change
|
||||||
### Added
|
### Added
|
||||||
- New `coffi.mem/null` var for implementing custom types
|
- New `coffi.mem/null` var for implementing custom types
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Upcall and downcall classes have been changed to be memoized, meaning ASM is no longer invoked every time a function is serialized, which should drastically improve performance where functions are serialized in a hot loop
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Usage of deprecated `(Class/STATIC_FIELD)` access pattern
|
- Usage of deprecated `(Class/STATIC_FIELD)` access pattern
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,53 +130,70 @@
|
||||||
[:invokevirtual (prim-classes prim-type) (unbox-fn-for-type prim-type) [prim]]]
|
[:invokevirtual (prim-classes prim-type) (unbox-fn-for-type prim-type) [prim]]]
|
||||||
[]))))
|
[]))))
|
||||||
|
|
||||||
(defn- downcall-class
|
(defn- downcall-class-ctor*
|
||||||
"Class definition for an implementation of [[IFn]] which calls a closed over
|
"Returns a function to construct a downcall class for the given `args` and `ret` types.
|
||||||
|
|
||||||
|
A downcall class is an implementation of [[IFn]] which calls a closed over
|
||||||
method handle without reflection, unboxing primitives when needed."
|
method handle without reflection, unboxing primitives when needed."
|
||||||
[args ret]
|
[args ret]
|
||||||
{:flags #{:public :final}
|
(let [klass (insn/define
|
||||||
:version 8
|
{:flags #{:public :final}
|
||||||
:super clojure.lang.AFunction
|
:version 8
|
||||||
:fields [{:name "downcall_handle"
|
:super clojure.lang.AFunction
|
||||||
:type MethodHandle
|
:fields [{:name "downcall_handle"
|
||||||
:flags #{:final}}]
|
:type MethodHandle
|
||||||
:methods [{:name :init
|
:flags #{:final}}]
|
||||||
:flags #{:public}
|
:methods [{:name :init
|
||||||
:desc [MethodHandle :void]
|
:flags #{:public}
|
||||||
:emit [[:aload 0]
|
:desc [MethodHandle :void]
|
||||||
[:dup]
|
:emit [[:aload 0]
|
||||||
[:invokespecial :super :init [:void]]
|
[:dup]
|
||||||
[:aload 1]
|
[:invokespecial :super :init [:void]]
|
||||||
[:putfield :this "downcall_handle" MethodHandle]
|
[:aload 1]
|
||||||
[:return]]}
|
[:putfield :this "downcall_handle" MethodHandle]
|
||||||
{:name :invoke
|
[:return]]}
|
||||||
:flags #{:public}
|
{:name :invoke
|
||||||
:desc (repeat (cond-> (inc (count args))
|
:flags #{:public}
|
||||||
(not (mem/primitive-type ret)) inc)
|
:desc (repeat (cond-> (inc (count args))
|
||||||
Object)
|
(not (mem/primitive-type ret)) inc)
|
||||||
:emit [[:aload 0]
|
Object)
|
||||||
[:getfield :this "downcall_handle" MethodHandle]
|
:emit [[:aload 0]
|
||||||
(when-not (mem/primitive-type ret)
|
[:getfield :this "downcall_handle" MethodHandle]
|
||||||
[[:aload 1]
|
(when-not (mem/primitive-type ret)
|
||||||
[:checkcast SegmentAllocator]])
|
[[:aload 1]
|
||||||
(map-indexed
|
[:checkcast SegmentAllocator]])
|
||||||
(fn [idx arg]
|
(map-indexed
|
||||||
[[:aload (cond-> (inc idx)
|
(fn [idx arg]
|
||||||
(not (mem/primitive-type ret)) inc)]
|
[[:aload (cond-> (inc idx)
|
||||||
(to-prim-asm arg)])
|
(not (mem/primitive-type ret)) inc)]
|
||||||
args)
|
(to-prim-asm arg)])
|
||||||
[:invokevirtual MethodHandle "invokeExact"
|
args)
|
||||||
(cond->>
|
[:invokevirtual MethodHandle "invokeExact"
|
||||||
(conj (mapv insn-layout args)
|
(cond->>
|
||||||
(insn-layout ret))
|
(conj (mapv insn-layout args)
|
||||||
(not (mem/primitive-type ret)) (cons SegmentAllocator))]
|
(insn-layout ret))
|
||||||
(to-object-asm ret)
|
(not (mem/primitive-type ret)) (cons SegmentAllocator))]
|
||||||
[:areturn]]}]})
|
(to-object-asm ret)
|
||||||
|
[:areturn]]}]})
|
||||||
|
ctor (.getConstructor klass
|
||||||
|
(doto ^"[Ljava.lang.Class;" (make-array Class 1)
|
||||||
|
(aset 0 MethodHandle)))]
|
||||||
|
(fn [^MethodHandle h]
|
||||||
|
(.newInstance ctor
|
||||||
|
(doto (object-array 1)
|
||||||
|
(aset 0 h))))))
|
||||||
|
|
||||||
|
(def ^:private downcall-class-ctor
|
||||||
|
"Returns a function to construct a downcall class for the given memoized `args` and `ret` types.
|
||||||
|
|
||||||
|
A downcall class is an implementation of [[IFn]] which calls a closed over
|
||||||
|
method handle without reflection, unboxing primitives when needed."
|
||||||
|
(memoize downcall-class-ctor*))
|
||||||
|
|
||||||
(defn- downcall-fn
|
(defn- downcall-fn
|
||||||
"Creates a function to call `handle` without reflection."
|
"Creates a function to call `handle` without reflection."
|
||||||
[handle args ret]
|
[handle args ret]
|
||||||
(insn/new-instance (downcall-class args ret) ^MethodHandle handle))
|
((downcall-class-ctor args ret) ^MethodHandle handle))
|
||||||
|
|
||||||
(defn ensure-symbol
|
(defn ensure-symbol
|
||||||
"Returns the argument if it is a [[MemorySegment]], otherwise
|
"Returns the argument if it is a [[MemorySegment]], otherwise
|
||||||
|
|
@ -462,50 +479,69 @@
|
||||||
"Set of primitive types which require 2 indices in the constant pool."
|
"Set of primitive types which require 2 indices in the constant pool."
|
||||||
#{::mem/double ::mem/long})
|
#{::mem/double ::mem/long})
|
||||||
|
|
||||||
(defn- upcall-class
|
(defn- upcall-class-ctor*
|
||||||
"Constructs a class definition for a class with a single method, `upcall`, which
|
"Returns a function to construct an upcall class for the given `arg-types` and `ret-types`.
|
||||||
boxes any primitives passed to it and calls a closed over [[IFn]]."
|
|
||||||
|
An upcall class is a class with a single method, `upcall`, which boxes any
|
||||||
|
primitives passed to it and calls a closed over [[IFn]]."
|
||||||
[arg-types ret-type]
|
[arg-types ret-type]
|
||||||
{:flags #{:public :final}
|
(let [klass (insn/define
|
||||||
:version 8
|
{:flags #{:public :final}
|
||||||
:fields [{:name "upcall_ifn"
|
:version 8
|
||||||
:type IFn
|
:fields [{:name "upcall_ifn"
|
||||||
:flags #{:final}}]
|
:type IFn
|
||||||
:methods [{:name :init
|
:flags #{:final}}]
|
||||||
:flags #{:public}
|
:methods [{:name :init
|
||||||
:desc [IFn :void]
|
:flags #{:public}
|
||||||
:emit [[:aload 0]
|
:desc [IFn :void]
|
||||||
[:dup]
|
:emit [[:aload 0]
|
||||||
[:invokespecial :super :init [:void]]
|
[:dup]
|
||||||
[:aload 1]
|
[:invokespecial :super :init [:void]]
|
||||||
[:putfield :this "upcall_ifn" IFn]
|
[:aload 1]
|
||||||
[:return]]}
|
[:putfield :this "upcall_ifn" IFn]
|
||||||
{:name :upcall
|
[:return]]}
|
||||||
:flags #{:public}
|
{:name :upcall
|
||||||
:desc (conj (mapv insn-layout arg-types)
|
:flags #{:public}
|
||||||
(insn-layout ret-type))
|
:desc (conj (mapv insn-layout arg-types)
|
||||||
:emit [[:aload 0]
|
(insn-layout ret-type))
|
||||||
[:getfield :this "upcall_ifn" IFn]
|
:emit [[:aload 0]
|
||||||
(loop [types arg-types
|
[:getfield :this "upcall_ifn" IFn]
|
||||||
acc []
|
(loop [types arg-types
|
||||||
idx 1]
|
acc []
|
||||||
(if (seq types)
|
idx 1]
|
||||||
(let [prim (mem/primitive-type (first types))]
|
(if (seq types)
|
||||||
(recur (rest types)
|
(let [prim (mem/primitive-type (first types))]
|
||||||
(conj acc [[(load-instructions prim :aload) idx]
|
(recur (rest types)
|
||||||
(to-object-asm (first types))])
|
(conj acc [[(load-instructions prim :aload) idx]
|
||||||
(cond-> (inc idx)
|
(to-object-asm (first types))])
|
||||||
(double-sized? prim)
|
(cond-> (inc idx)
|
||||||
inc)))
|
(double-sized? prim)
|
||||||
acc))
|
inc)))
|
||||||
[:invokeinterface IFn "invoke" (repeat (inc (count arg-types)) Object)]
|
acc))
|
||||||
(to-prim-asm ret-type)
|
[:invokeinterface IFn "invoke" (repeat (inc (count arg-types)) Object)]
|
||||||
[(return-for-type ret-type :areturn)]]}]})
|
(to-prim-asm ret-type)
|
||||||
|
[(return-for-type ret-type :areturn)]]}]})
|
||||||
|
ctor (.getConstructor klass
|
||||||
|
(doto ^"[Ljava.lang.Class;" (make-array Class 1)
|
||||||
|
(aset 0 IFn)))]
|
||||||
|
(fn [^IFn f]
|
||||||
|
(.newInstance ctor
|
||||||
|
(doto (object-array 1)
|
||||||
|
(aset 0 f))))))
|
||||||
|
|
||||||
|
(def ^:private upcall-class-ctor
|
||||||
|
"Returns a function to construct an upcall class for the given memoized `arg-types` and `ret-types`.
|
||||||
|
|
||||||
|
An upcall class is a class with a single method, `upcall`, which boxes any
|
||||||
|
primitives passed to it and calls a closed over [[IFn]]."
|
||||||
|
(memoize upcall-class-ctor*))
|
||||||
|
|
||||||
(defn- upcall
|
(defn- upcall
|
||||||
"Constructs an instance of [[upcall-class]], closing over `f`."
|
"Constructs an instance of an upcall class, closing over `f`.
|
||||||
|
|
||||||
|
See [[upcall-class-ctor]]."
|
||||||
[f arg-types ret-type]
|
[f arg-types ret-type]
|
||||||
(insn/new-instance (upcall-class arg-types ret-type) ^IFn f))
|
((upcall-class-ctor arg-types ret-type) ^IFn f))
|
||||||
|
|
||||||
(defn- method-type
|
(defn- method-type
|
||||||
"Gets the [[MethodType]] for a set of `args` and `ret` types."
|
"Gets the [[MethodType]] for a set of `args` and `ret` types."
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue