Oh yeah. I just wanted to actually measure the tradeoff for a common-ish use-case (web). I really want to build a $10/yr hobby project setup and it seems like native image might be important in that.
@dominicm for small hobby projects this may also be interesting: https://twitter.com/borkdude/status/1308737740788375552
Very happy to finally have figured out why clojure.pprint was bloating GraalVM binaries so much: https://github.com/borkdude/pprint tl;dr with only a minor change this can be fixed. If core team is interested, I'll make a patch. Alternatively I'll maintain a (cleaned up) fork of pprint with this minor adaptation.
(and now I can finally swap out fipp with real pprint including cl-format in bb)
what's the summary?
> table-ize uses find-var which tends to bloat GraalVM native images. > The find-var can be avoided by making write-option-table a table of keywords to vars.
oh, I see there are words... reading :)
so change keyword->symbol to keyword->var ?
yes
seems like that has negative impacts on non-graal load time?
why?
finding a var takes time?
I think there are ways around that (like lazy loading those)
but that's the kind of thing I'd look at. also whether anyone reaches into that private table ever
I'm not sure what you mean. This is the change I made (babashka.pprint ~ clojure.pprint).
(def ^{:private true} write-option-table
{;:array *print-array*
:base #'babashka.pprint/*print-base*
;;:case *print-case*,
:circle #'babashka.pprint/*print-circle*
;;:escape *print-escape*,
;;:gensym *print-gensym*,
:length #'clojure.core/*print-length*
:level #'clojure.core/*print-level*
:lines #'babashka.pprint/*print-lines*
:miser-width #'babashka.pprint/*print-miser-width*
:dispatch #'babashka.pprint/*print-pprint-dispatch*
:pretty #'babashka.pprint/*print-pretty*
:radix #'babashka.pprint/*print-radix*
:readably #'clojure.core/*print-readably*
:right-margin #'babashka.pprint/*print-right-margin*
:suppress-namespaces #'babashka.pprint/*print-suppress-namespaces*})
(the comments were already in there)
my point is that when you load this ns, that's a lot more work than initializing the same table with symbols
isn't this AOT-ed?
yes, but that doesn't matter
you can't encode a var in bytecode
oh I see. well yeah, we can make it a delay probably
you have to encode the instructions to load the var
I mean you could even just delay the whole table and then force when needed
yeah, that's what I meant. But what are we talking about, nano-seconds probably?
I'd guess more than that
do you want to be the bad guy making Clojure startup slower? :)
or is this some ulterior motive to force more people to graal?
;)
pprint is already slow too load ;) j/k. yeah, I'd be happy to look into if this makes a difference or not. I didn't know (var foo.bar/baz)
had an impact on startup time, so TIL
wrapping the table in a delay works for graalvm native-image as well, just tested it
doing work is usually slower than not doing it :)
btw, pprint also uses a macro called binding-map
which is I think the same as with-bindings
that doesn't surprise me - pprint is kind of it's own body of work grafted into clojure :)
and it seems that write
calls table-ize
on every call, which is not needed since this is a static set of data
it has a lot of things in it that give me pause when I read it, particularly from a perf pov
eh no I take that back, table-ize
has options
yeah, it's pretty wild
a lot of stuff is ported from common lisp stuff
I'll do some measurements on startup time and I'll get back to you
I don't know if you've ever stumbled on it but there are some pprint docs written by Tom from when this was integrated
http://clojure.github.io/clojure/doc/clojure/pprint/PrettyPrinting.html
and http://clojure.github.io/clojure/doc/clojure/pprint/CommonLispFormat.html
but back to original question - feel free to file a jira with a patch and I'll take a look for 1.10.2
;; with delay
user=> (time (dotimes [i 100] (require '[babashka.pprint] :reload)))
"Elapsed time: 21165.989 msecs"
nil
;; without delay
user=> (time (dotimes [i 100] (require '[babashka.pprint] :reload)))
"Elapsed time: 24007.1286 msecs"
nil
;; with delay
user=> (time (dotimes [i 100] (require '[babashka.pprint] :reload)))
"Elapsed time: 27414.7834 msecs"
nil
;; without delay
user=> (time (dotimes [i 100] (require '[babashka.pprint] :reload)))
"Elapsed time: 30943.7437 msecs"
I don't know if you saw that CLJ-2571 and CLJ-2572 went in
yeah, I saw it, nice :)
I'll have a look later this week, thanks for the advice so far
so, not nanos eh
although I guess that's not symbols to vars
keywords to vars
I meant changing the map values from symbols to vars
Note sure what you mean. This is the original table: https://github.com/clojure/clojure/blob/4ef4b1ed7a2e8bb0aaaacfb0942729252c2c3091/src/clj/clojure/pprint/pprint_base.clj#L118
I just meant those timings above were comparisons from delay to no delay (I assume), not comparisons from table of symbols to table of vars
that's correct
I'll do those timings too. getting late here, going afk :)
np