graalvm

Discuss GraalVM related topics. Use clojure 1.10.2 or newer for all new projects. Contribute to https://github.com/clj-easy/graal-docs and https://github.com/BrunoBonacci/graalvm-clojure. GraalVM slack: https://www.graalvm.org/slack-invitation/.
dominicm 2020-09-23T13:00:36.000900Z

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.

borkdude 2020-09-23T13:12:56.001200Z

@dominicm for small hobby projects this may also be interesting: https://twitter.com/borkdude/status/1308737740788375552

borkdude 2020-09-23T21:59:00.002700Z

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.

👍 1
borkdude 2020-09-23T21:59:22.003300Z

(and now I can finally swap out fipp with real pprint including cl-format in bb)

🎉 2
alexmiller 2020-09-23T22:05:43.004300Z

what's the summary?

borkdude 2020-09-23T22:06:18.004600Z

> 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.

alexmiller 2020-09-23T22:06:28.004800Z

oh, I see there are words... reading :)

alexmiller 2020-09-23T22:08:28.005200Z

so change keyword->symbol to keyword->var ?

borkdude 2020-09-23T22:08:32.005400Z

yes

alexmiller 2020-09-23T22:08:51.005700Z

seems like that has negative impacts on non-graal load time?

borkdude 2020-09-23T22:09:08.005900Z

why?

alexmiller 2020-09-23T22:09:23.006100Z

finding a var takes time?

alexmiller 2020-09-23T22:10:09.006700Z

I think there are ways around that (like lazy loading those)

alexmiller 2020-09-23T22:10:40.007600Z

but that's the kind of thing I'd look at. also whether anyone reaches into that private table ever

borkdude 2020-09-23T22:10:47.007800Z

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*})

borkdude 2020-09-23T22:10:57.008Z

(the comments were already in there)

alexmiller 2020-09-23T22:11:44.008500Z

my point is that when you load this ns, that's a lot more work than initializing the same table with symbols

borkdude 2020-09-23T22:11:59.008900Z

isn't this AOT-ed?

alexmiller 2020-09-23T22:12:21.009200Z

yes, but that doesn't matter

alexmiller 2020-09-23T22:12:31.009600Z

you can't encode a var in bytecode

borkdude 2020-09-23T22:12:36.009900Z

oh I see. well yeah, we can make it a delay probably

alexmiller 2020-09-23T22:12:39.010Z

you have to encode the instructions to load the var

alexmiller 2020-09-23T22:13:36.010600Z

I mean you could even just delay the whole table and then force when needed

borkdude 2020-09-23T22:13:59.011100Z

yeah, that's what I meant. But what are we talking about, nano-seconds probably?

alexmiller 2020-09-23T22:15:16.011500Z

I'd guess more than that

alexmiller 2020-09-23T22:15:39.011800Z

do you want to be the bad guy making Clojure startup slower? :)

alexmiller 2020-09-23T22:16:05.012300Z

or is this some ulterior motive to force more people to graal?

alexmiller 2020-09-23T22:16:07.012500Z

;)

borkdude 2020-09-23T22:16:43.013200Z

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

borkdude 2020-09-23T22:17:50.013700Z

wrapping the table in a delay works for graalvm native-image as well, just tested it

alexmiller 2020-09-23T22:18:21.014300Z

doing work is usually slower than not doing it :)

borkdude 2020-09-23T22:18:23.014400Z

btw, pprint also uses a macro called binding-map which is I think the same as with-bindings

alexmiller 2020-09-23T22:18:55.015100Z

that doesn't surprise me - pprint is kind of it's own body of work grafted into clojure :)

borkdude 2020-09-23T22:19:10.015700Z

and it seems that write calls table-ize on every call, which is not needed since this is a static set of data

alexmiller 2020-09-23T22:19:44.016500Z

it has a lot of things in it that give me pause when I read it, particularly from a perf pov

borkdude 2020-09-23T22:19:46.016700Z

eh no I take that back, table-ize has options

borkdude 2020-09-23T22:19:58.017Z

yeah, it's pretty wild

alexmiller 2020-09-23T22:20:17.017400Z

a lot of stuff is ported from common lisp stuff

borkdude 2020-09-23T22:20:40.017700Z

I'll do some measurements on startup time and I'll get back to you

alexmiller 2020-09-23T22:22:50.018200Z

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

alexmiller 2020-09-23T22:24:45.019400Z

but back to original question - feel free to file a jira with a patch and I'll take a look for 1.10.2

borkdude 2020-09-23T22:25:04.020Z

;; 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"

alexmiller 2020-09-23T22:25:15.020400Z

I don't know if you saw that CLJ-2571 and CLJ-2572 went in

borkdude 2020-09-23T22:25:42.020600Z

yeah, I saw it, nice :)

borkdude 2020-09-23T22:26:47.021400Z

I'll have a look later this week, thanks for the advice so far

alexmiller 2020-09-23T22:27:47.021600Z

so, not nanos eh

alexmiller 2020-09-23T22:28:27.022Z

although I guess that's not symbols to vars

borkdude 2020-09-23T22:29:13.022200Z

keywords to vars

alexmiller 2020-09-23T22:30:46.022700Z

I meant changing the map values from symbols to vars

borkdude 2020-09-23T22:33:01.023Z

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

alexmiller 2020-09-23T22:33:57.023900Z

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

borkdude 2020-09-23T22:34:15.024100Z

that's correct

borkdude 2020-09-23T22:34:50.024800Z

I'll do those timings too. getting late here, going afk :)

alexmiller 2020-09-23T22:35:03.025Z

np