Last active
May 26, 2019 17:08
-
-
Save ztellman/5603216 to your computer and use it in GitHub Desktop.
an exploration of the memory implications of call-site caching for protocols
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; let's create a simple protocol that just returns a number | |
user> (defprotocol NumberP (number [_])) | |
NumberP | |
;; now we'll create an implementation that always returns '1' | |
user> (deftype One [] NumberP (number [_] 1)) | |
user.One | |
;; unsurprisingly, this type only has a single static value, which wraps the '1' | |
> (-> One .getDeclaredFields seq) | |
(#<Field public static final java.lang.Object user.One.const__0>) | |
;; but what if we define a type in terms of another object implementing this protocol? | |
user> (deftype Two [one] NumberP (number [_] (+ (number one) (number one)))) | |
user.Two | |
;; here we see the underlying implementation of how protocols are implemented: for each dispatch | |
;; to a protocol within the type, we gain three implicit per-instance fields on the object | |
user> (-> Two .getDeclaredFields seq) | |
(#<Field public static final clojure.lang.Var user.Two.const__0> #<Field public static final clojure.lang.Var user.Two.const__1> #<Field public final java.lang.Object user.Two.one> #<Field private java.lang.Class user.Two.__cached_class__0> #<Field private clojure.lang.AFunction user.Two.__cached_proto_fn__0> #<Field private clojure.lang.IFn user.Two.__cached_proto_impl__0> #<Field private java.lang.Class user.Two.__cached_class__1> #<Field private clojure.lang.AFunction user.Two.__cached_proto_fn__1> #<Field private clojure.lang.IFn user.Two.__cached_proto_impl__1>) | |
user> (-> Two .getDeclaredFields count) | |
9 | |
;; this is unavoidable if we don't know what type the object is, but even if we typehint the member | |
;; so we can directly dispatch, the memory overhead (and possibly also the invocation overhead?) remains | |
user> (deftype Two [^One one] NumberP (number [_] (+ (number one) (number one)))) | |
user.Two | |
user> (-> Two .getDeclaredFields count) | |
9 | |
;; for each new callsite, we gain three additional per-instance fields, without limit | |
user> (deftype Three [^One one] NumberP (number [_] (+ (number one) (number one) (number one)))) | |
user.Three | |
user> (-> Three .getDeclaredFields count) | |
12 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It seems to be better in Clojure 1.10: