Fork me on GitHub

anyone here running into performance issues with the evaluation of render-fns? With 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

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


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


yeah sounds good


I'll take a stab at it

💯 2
🌸 2

@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?


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


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


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


added some text


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


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


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


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


@mkvlr how did the prose talk negatively about SCI?


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


yeah also can’t think of something immediatly


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


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


@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


@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


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


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


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


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


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


volatile doesn't have this problem


(vswap! (volatile! 0) inc)


for performance without watches, volatile is a great option

Sam Ritchie19:05:24

that’s excellent


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


@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


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


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!


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

👍 2

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


"that case" I mean


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:
Calling `!` on shutdown...
Worker shutdown.


@U017QJZ9M7W should I bump some clerk util you made?


I meant the manifold / helitorus example


if we get similar performance to advanced compiled cljs with cherry

Sam Ritchie19:05:25

yeah, bump to 1e0c4473073cadb35f938c6cea5dceeb3f5f95bd @borkdude


bump what ?




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


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


@mkvlr did clerk recently introduce a breaking change here?


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


@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?


@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

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


 "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.error/class clojure.lang.ExceptionInfo,
  :clojure.error/line 150,
  "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},
  [{:type clojure.lang.ExceptionInfo,
    "`nextjournal.clerk/show!` encountered an eval error with: `\"dev/mathbox/notebook.clj\"`",
     {:nav-path "dev/mathbox/notebook.clj",
      [{:type :code,
        "^#:nextjournal.clerk\n{:toc true\n :no-cache true\n :visibility :hide-ns}\n(ns mathbox.notebook\n  (:require [ :as docs]\n            [ :refer [show-sci]]\n            [nextjournal.clerk :as clerk]))",
        :loc {:line 1, :end-line 8, :column 1, :end-column 44}}
       {:type :code,
        "^{::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,
        {:type :doc,
         [{:type :heading,
           :content [{:type :text, :text "MathBox.cljs"}],
           :heading-level 1,
           :attrs {:id "mathbox.cljs"}}
          {:type :paragraph,
           [{:type :text, :text "A "}
            {:type :link,


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?


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


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


yeah we only support a single form in the latest


to bring it in line with clojure.core/eval


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

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


should work without the html-render


Do you mean like:

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


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.


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


no, keep eval-cljs-str


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


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


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


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

😆 2

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


oh lol, this works:


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


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


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


I updated the code here now: should run with bb clerk-watch, the notebook is heli.cljc


Anyway, back to a non-contrived clerk book example


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


Not really sure how to trigger the expensive operation.


pushed to branch clerk / cherry-book


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


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


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


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

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


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


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


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


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…