Fork me on GitHub
#clerk
<
2023-05-22
>
mkvlr07:05:56

anyone here running into performance issues with the evaluation of render-fns? With https://github.com/nextjournal/clerk/pull/486 we’ve made it easy to try out 🍒 cherry as an alternative evaluator (to sci) which should give better performance. Curious to hear if folks have been running into cases where that matters.

🎉 6
borkdude08:05:49

@mkvlr Perhaps we can add an entry to the clerk book for this?

mkvlr08:05:44

want to take a stab at this? I’m not sure if it’s better in Customizations or Viewers, the division doensn’t feel too clear atm

borkdude08:05:43

yeah sounds good

borkdude08:05:50

I'll take a stab at it

💯 2
🌸 2
borkdude12:05:02

@mkvlr the book already explicitly writes :render-evaluator :sci in some places, but 1. this is the default so maybe not necessary? 2. this key is never explained anywhere, I think?

borkdude12:05:04

oh I see, it's just in the results because of show-raw-value

borkdude12:05:13

it's bit unfortunate the folded code can't be found using ctrl-f

borkdude14:05:04

@mkvlr How about something like this? (I had to implement with-out-str as a result of this example in cherry ;))

borkdude14:05:05

added some text

mkvlr17:05:51

think we might want to talk a bit less negatively about sci and flesh out the use cases a bit more

mkvlr17:05:19

like can we come up with a real example where the performance actually matters?

mkvlr17:05:35

like what’s a good example where you’d want to write a loop in a render-fn?

mkvlr17:05:30

and I think it’s cool to show the timing difference but think it might be nice to extend a bit on when that really matters and that the code shown is the pathological case

borkdude17:05:11

@mkvlr how did the prose talk negatively about SCI?

borkdude17:05:22

I'm all for a more real-life use case, but it's hard to come up with one that is fairly self-contained.... maybe a FPS measurer like @U017QJZ9M7W had

mkvlr18:05:39

yeah also can’t think of something immediatly

mkvlr18:05:47

re negativity: slow-viewer sounds slow 🙃 it’s still plenty fast in my experience (surprisingly fast even)

Sam Ritchie18:05:10

Yeah any real-time chart measuring some think or animation loop would be a good example

mkvlr18:05:23

so something that does something on mouse-move for example and draws a FPS counter?

borkdude18:05:33

@mkvlr ok, I'll change slow-viewer to a more neutral name :)

Sam Ritchie18:05:44

I never pinned down what was slow about the examples that weren’t straight animation updates

Sam Ritchie18:05:03

Usually there was a reagent atom state update then a read that did some work

borkdude18:05:42

@U017QJZ9M7W What is slow is the giant math function you had in there, if you have a giant function doing math in loops it's going to be slower in SCI (12-20fps or so?). With cherry this should be lots faster

mkvlr18:05:32

though I think for that stuff @U017QJZ9M7W is now using emmy’s js compiler, so it’s also fast

borkdude18:05:56

Yes, but I did port his slow example to cherry, which also made it to 60fps

Sam Ritchie18:05:17

Yeah that’s right , though if I wrote a function by hand that I couldn’t use Emmy for (easy to do with interval reagent lookups etc) cherry would win the day

Sam Ritchie18:05:44

Emmy’s final JS step did the same thing cherry does, the output is the same… which implies I should probably emit just an s expression and let cherry do the final work

Sam Ritchie18:05:03

Instead of splicing in a form with js/Function

Sam Ritchie18:05:11

To my viewer

borkdude18:05:22

I think deref in SCI is also slower than it needs to be, but this is due to special casing of the IDeref protocol

user=> (let [atm (atom 0)] (time (dotimes [i 1000000] atm)))
"Elapsed time: 101.531917 msecs"
user=> (let [atm (atom 0)] (time (dotimes [i 1000000] @atm)))
"Elapsed time: 312.373792 msecs"
So perhaps that also contributed negatively

borkdude19:05:15

I think I have the helitaurus example somewhere, let me check if swapping out deref makes any significant difference

Sam Ritchie19:05:54

There was another pendulum example where I had one component running a swap! On some state at 60fps and then the pendulum component dereffing

Sam Ritchie19:05:34

I was using (.-state the-atom) to get around reagent’s deref tracking, but that failed with cursors so wasn’t great

borkdude19:05:23

I think I can speed up swap! and deref in SCI but never realized this may be used in very hot loops

Sam Ritchie19:05:01

wdyt, should be using some other state mechanism for holding the physics state?

Sam Ritchie19:05:17

I felt a little uneasy about using swap! and deref too

borkdude19:05:26

volatile doesn't have this problem

borkdude19:05:41

(vswap! (volatile! 0) inc)

borkdude19:05:55

for performance without watches, volatile is a great option

Sam Ritchie19:05:24

that’s excellent

borkdude19:05:27

still the deref function is used to get the value out though

Sam Ritchie19:05:41

@borkdude and I guess I could recover the ability to reactively update the state by hitting some :updated field in a reagent atom…

Sam Ritchie19:05:51

not saying this is a good visualization 🙂

Sam Ritchie19:05:52

volatlile! works great in situations where mathbox etc are polling, and then if I want someone to respond to a state update I can do something better

mkvlr19:05:50

@U017QJZ9M7W are the viewer fns in this example compiled or running through sci?

Sam Ritchie19:05:36

compiled in that example

Sam Ritchie19:05:25

which is a “failure” of this demo; I want something like this to by dynamically constructed, which would send the component to either SCI or cherry. but I (via Emmy) can still build a fast function to run in the hot loop

Sam Ritchie19:05:05

here’s what it SHOULD feel like

Sam Ritchie19:05:01

a “scene” built out of objects returned by manifold as server-side fragments using our render-fn-creating trick, @mkvlr, instead of me pre-compiling anything. EDIT sorry, I actually DO pre-compile a Manifold component built out of some lower-level mathbox primitives and then use that higher-level API

borkdude19:05:38

@U017QJZ9M7W Can you tell me how to build that helitorus example again? I forgot about it

borkdude19:05:24

bb clerk-watch - o wait, I was using a local/root thing

Sam Ritchie19:05:13

is it working? I have shuffled so much since I made that example, I can recreate it if we need to!

mkvlr19:05:34

cool, curious to learn if the cherry setting helps in that case

👍 2
borkdude19:05:30

> cool, curious to learn if the cherry setting helps in that case what were you referring to?

borkdude19:05:42

"that case" I mean

borkdude19:05:02

The required namespace "nextjournal.clerk.static-app" is not available.

Execution error (ExceptionInfo) at nextjournal.clerk.eval/eval+cache! (eval.clj:150).
Execution error (ArityException) at nextjournal.clerk/eval-cljs (clerk.clj:366).
Wrong number of args (3) passed to: nextjournal.clerk.viewer/eval-cljs


Full report at:
/var/folders/j9/xmjlcym958b1fr0npsp9msvh0000gn/T/clojure-7474133642228579913.edn
Calling `mentat.clerk-utils.build/halt!` on shutdown...
Worker shutdown.

borkdude19:05:23

@U017QJZ9M7W should I bump some clerk util you made?

mkvlr19:05:34

I meant the manifold / helitorus example

mkvlr19:05:02

if we get similar performance to advanced compiled cljs with cherry

Sam Ritchie19:05:25

yeah, bump to 1e0c4473073cadb35f938c6cea5dceeb3f5f95bd @borkdude

borkdude19:05:34

bump what ?

borkdude19:05:15

clerk-utils?

borkdude19:05:19

I noticed you're using io.github.mentat-collective/clerk-utils in the README but in this Mathbox.cljs repo I found org.mentat/clerk-utils as the lib name - better to avoid this, this can lead to unpredictable versions on the classpath

borkdude19:05:33

Still getting Wrong number of args (3) passed to: nextjournal.clerk.viewer/eval-cljs with bumped clerk-utils

Sam Ritchie19:05:01

Yeah that’s a bug for sure in mathbox.cljs

borkdude19:05:02

@mkvlr did clerk recently introduce a breaking change here?

borkdude19:05:20

it seems eval-cljs used to take & forms, but now takes a single form + options.

borkdude19:05:11

@U017QJZ9M7W if you could fix that in clerk-utils perhaps, then I can try getting this helitorus up to date here

Sam Ritchie19:05:38

Oh yeah i will take a look

Sam Ritchie19:05:52

@borkdude I don’t see where I was using eval-cljs in clerk-utils - I thought I was just doing that for the namespace aliases, but I didn’t think it was in the library?

mkvlr19:05:05

@borkdude yes, haven’t added that to the changelog yet 🙈 Realized that it’s better to be consistent with eval which also just takes a single form

👍 2
borkdude20:05:16

maybe it's in clerk itself?

Execution error (ExceptionInfo) at nextjournal.clerk.eval/eval+cache! (eval.clj:150).
Execution error (ArityException) at nextjournal.clerk/eval-cljs (clerk.clj:366).
Wrong number of args (3) passed to: nextjournal.clerk.viewer/eval-cljs

borkdude20:05:08

{:clojure.main/message
 "Execution error (ExceptionInfo) at nextjournal.clerk.eval/eval+cache! (eval.clj:150).\nExecution error (ArityException) at nextjournal.clerk/eval-cljs (clerk.clj:366).\nWrong number of args (3) passed to: nextjournal.clerk.viewer/eval-cljs\n\n",
 :clojure.main/triage
 {:clojure.error/class clojure.lang.ExceptionInfo,
  :clojure.error/line 150,
  :clojure.error/cause
  "Execution error (ArityException) at nextjournal.clerk/eval-cljs (clerk.clj:366).\nWrong number of args (3) passed to: nextjournal.clerk.viewer/eval-cljs\n",
  :clojure.error/symbol nextjournal.clerk.eval/eval+cache!,
  :clojure.error/source "eval.clj",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.ExceptionInfo,
    :message
    "`nextjournal.clerk/show!` encountered an eval error with: `\"dev/mathbox/notebook.clj\"`",
    :data
    {:nextjournal.clerk/doc
     {:nav-path "dev/mathbox/notebook.clj",
      :blocks
      [{:type :code,
        :text
        "^#:nextjournal.clerk\n{:toc true\n :no-cache true\n :visibility :hide-ns}\n(ns mathbox.notebook\n  (:require [mentat.clerk-utils.docs :as docs]\n            [mentat.clerk-utils.show :refer [show-sci]]\n            [nextjournal.clerk :as clerk]))",
        :loc {:line 1, :end-line 8, :column 1, :end-column 44}}
       {:type :code,
        :text
        "^{::clerk/visibility {:code :hide :result :hide}}\n(clerk/eval-cljs\n ;; These aliases only apply inside this namespace.\n '(require '[mathbox.core :as mathbox])\n '(require '[mathbox.primitives :as mb])\n '(require '[reagent.core :as reagent]))",
        :loc {:line 10, :end-line 15, :column 1, :end-column 41}}
       {:type :markdown,
        :doc
        {:type :doc,
         :content
         [{:type :heading,
           :content [{:type :text, :text "MathBox.cljs"}],
           :heading-level 1,
           :attrs {:id "mathbox.cljs"}}
          {:type :paragraph,
           :content
           [{:type :text, :text "A "}
            {:type :link,

borkdude20:05:26

maybe I'm doing this myself in the notebook

Sam Ritchie20:05:50

"^{::clerk/visibility {:code :hide :result :hide}}\n(clerk/eval-cljs\n ;; These aliases only apply inside this namespace.\n '(require '[mathbox.core :as mathbox])\n '(require '[mathbox.primitives :as mb])\n '(require '[reagent.core :as reagent]))",

Sam Ritchie20:05:54

it’s this form in the notebook right?

borkdude20:05:20

I haven't even executed the notebook, it happens during bb clerk-watch

borkdude20:05:33

more tomorrow... gotta run now

Sam Ritchie20:05:38

oh I didn’t realize what project you were in, I thought you had your own project with just this one file

Sam Ritchie20:05:52

this is where that form lives

Sam Ritchie20:05:18

yeah I guess it fails on dev/mathbox/notebook.clj since I marked that as the index

mkvlr20:05:09

yeah we only support a single form in the latest

mkvlr20:05:20

to bring it in line with clojure.core/eval

mkvlr20:05:32

let me know if that’s hard for you to change, then we can reconsider this change and certainly need to add it to the changelog

Sam Ritchie20:05:48

it’s no problem, I think I will honestly not need eval-cljs the same way once I push through my “build a viewer a la carte” work

👍 2
borkdude20:05:29

I'm running into one of those "last time I checked I got proper HTML but now it's a CLJS data structure being rendered again" problems

mkvlr06:05:30

should work without the html-render

borkdude09:05:39

Do you mean like:

(clerk/html
 {:nextjournal.clerk/render-evaluator :cherry
  :evaluator :cherry}
 '[Helitorus !state cake2])

borkdude09:05:57

I tried this but I got:

react-dom.development.js:188 Warning: <Helitorus /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.

borkdude09:05:12

but maybe this is another unrelated problem, I'll dig in further

mkvlr09:05:31

no, keep eval-cljs-str

mkvlr09:05:03

but nextjournal.clerk.render/html-render should be nextjournal.clerk.viewer/html

borkdude09:05:43

@mkvlr This utterly confuses me. Especially since the viewer namespace in SCI is pointing to some things in the render namespace and cherry preserves the namespaces as they are in CLJS. https://github.com/nextjournal/clerk/blob/4b959dff5c26d0edba14456f2061ec639af7f11b/src/nextjournal/clerk/sci_env.cljs#L106-L108

mkvlr10:05:48

agree, confuses me as well, it’s on my list of things to clean up

mkvlr10:05:11

I’ve tried before but always ran into the halting problem

😆 2
borkdude12:05:43

@mkvlr Can you guide me what to do here then? Or maybe @U9EQP1K0X?

borkdude12:05:44

oh lol, this works:

borkdude12:05:19

SCI: 9fps (when cake2 is defined by SCI instead of cherry, with cherry it's 60fps)

borkdude12:05:18

overriding deref and swap! in SCI bumps it to 10fps, not that much difference

borkdude12:05:56

so my experiment for helitorus is over, the SCI improvement for swap/deref won't help much

borkdude12:05:50

I updated the code here now: https://github.com/borkdude/MathBox.cljs should run with bb clerk-watch, the notebook is heli.cljc

borkdude12:05:02

Anyway, back to a non-contrived clerk book example

borkdude13:05:09

Porting the FPS thing worked: But even with SCI it gets 61FPS, so I'll try throwing in some math heavy stuff

borkdude13:05:53

Not really sure how to trigger the expensive operation.

borkdude13:05:04

pushed to branch clerk / cherry-book

borkdude14:05:19

drat, having both SCI + cherry in one notebook will just show the same fps for both, since one apparently slows down the other, no matter how fast the other is

borkdude14:05:29

which makes sense, but showing the different FPS won't work then either

borkdude14:05:59

I do see a difference, just not when they are both rendered.

borkdude14:05:46

maybe I can hide one using a toggle

Sam Ritchie19:05:26

Another performance Q for @borkdude and @mkvlr, just making a new thread so I don’t smash the other one…

Sam Ritchie19:05:39

in my quest for performance, I have the Emmy compiler take “parameters” like the torus radius, stuff I can change with sliders, as a mutable java array, so that lookups are as cheap as possible in JS (vs calling deref) Leva updates a map inside of an atom when you change sliders. So I was using a reaction to create a mutable array off of the slider values every time they change, since changing values is rare and reading is common https://github.com/sritchie/clojure-conj-2023/blob/main/src/conj/custom.cljs#L28-L34

Sam Ritchie19:05:13

this “works”, except the reaction doesn’t run unless I render the mutable array with [nextjournal.clerk.render/inspect @!arr]

Sam Ritchie19:05:12

as I’m typing this I’m realizing I should just buckle down and use my own add-watch to construct the mutable array… is that right?

Sam Ritchie19:05:30

alternatively maybe deref + map lookups are actually pretty cheap and I should back out this mutable array thing

mkvlr19:05:33

if you’re thinking of doing add-watch that sounds almost like what a reagent atom is there for

Sam Ritchie19:05:01

yeah, but I want to run a side effect based on a change to the reagent atom

mkvlr19:05:24

but as always with performance optimizations we should start with measuring 🙃

💯 2
Sam Ritchie19:05:27

and the only way I can get reagent to do the side effect is with a reaction, and for THAT to run I have to render the reaction somewhere

mkvlr19:05:32

a, so not render but run something else?

Sam Ritchie19:05:48

I ask for these params in a hot loop that’s always running at 60fps

Sam Ritchie19:05:49

gotta run for a bit but here is the code for that fps monitor https://mrdoob.github.io/stats.js/

jackrusher08:05:45

Are there consumer-grade computers where this operation takes 16ms? On my laptop, I can reset! an atom a million times per frame at 60fps…