Fork me on GitHub
#fulcro
<
2019-09-06
>
maxt15:09:06

I'm seeing slow renders caused by calls to com.fulcrologic.fulcro.algorithms.do-not-use/conform! when using localized-dom. I have a repro here https://github.com/maxthoursie/fulcro3-conform-repro 175 ms of a 280 ms render is spent conforming. I think conform is only meant to be called during macro expansion, so I think the bug is that it's somehow leaking into cljs, but I haven't got further in understanding why it's happening.

maxt15:09:06

The repro component is just a

(dom/ul (for [i (range 1 100)]
                     (dom/li (str "Index " i))))

tony.kay15:09:16

@maxt hm…yeah, I thought that call was limited to uses in the macro expansion..it is a bug if it is being called at runtime.

tony.kay15:09:29

so many competing concerns in code 😜

simple_smile 4
maxt15:09:40

The spec warnings for macros in fulcro have been really helpful. It's a little scary though that it apperently is easy to make it leak.

tony.kay15:09:45

oh bummer…yeah, the macroexpansion does include it

tony.kay15:09:02

no no…this isn’t a macro problem

tony.kay15:09:07

this is a me problem 😄

maxt15:09:15

haha 🙂

tony.kay15:09:44

If you change your code to pass a map it’ll macroexpand and be fast

tony.kay15:09:55

(dom/ul {} ...)

maxt15:09:55

I'll try

tony.kay15:09:17

if you don’t pass a map, then it has to use the function version…and the function version (incorrecly) uses conform!

maxt15:09:12

Hm, it does remove the call to do-not-use/conform!, but re_conform is still getting called. 108 / 150 ms still spend in spec/re_conform.

tony.kay15:09:45

you sure you don’t have instrument on or something?

tony.kay15:09:57

and you put a props map on ul and li?

maxt15:09:04

no sorry it's still from do-not-use/conform!

maxt15:09:33

(dom/ul {}
                   (for [i (range 1 100)]
                     (dom/li {} (str "Index " i)))))

maxt15:09:05

this is a fresh project so no instrumentation or anything should be activated.

tony.kay15:09:30

ok, I’ve opened an issue…try it with normal DOM just for my sanity as well

maxt15:09:45

Regular dom does not call conform and frame_render/render! takes about 50 ms instead of 250 ms

tony.kay15:09:52

ok, and without {}?

tony.kay15:09:57

(thanks for checking, BTW)

maxt15:09:27

The same result without {}, no conforms.

maxt15:09:41

You're very welcome, thank you for looking into it.

tony.kay15:09:25

ok, so the regular DOM is fast

tony.kay15:09:29

but localized dom is not

tony.kay15:09:05

I’ll move the issue

maxt15:09:53

If I just update the component, the difference is 80 ms to 8 ms.

tony.kay15:09:32

So, just FYI this is not high on my priority list. You can easily use regular DOM where you don’t need the localized classes, or destructure the classnames. Open to a PR if you wanted to work on it, but I probably won’t for some time.

tony.kay15:09:56

i.e. there are tons of workarounds that are easy

tony.kay15:09:21

and I have much more pressing things to do with my time

maxt15:09:24

Ok. I understand, and I believe your prioritization is right. I really do like the localized-dom though so I might look into it.

exit215:09:16

Has anyone seen an issue where after building a production version (running through closure compiler) of a Fulcro app that click handlers stop randomly working?

tony.kay15:09:25

@njj that probably means that you used a name in inconsistent ways where it did not get renamed in some places, but did in others….depends on what you mean by “random”. If you mean “some stop working”, then that could be it. For example, if you make an onclick in your initLocalState and store it using (set~ (.-operation this) ...) then operation can get renamed

tony.kay15:09:40

later using gobj/get "operation" would fail to find it

exit215:09:41

I realize this is a very vague question, just wondering if the symptoms are something that anyone as seen before

exit215:09:43

Yeah it stops working

exit215:09:04

(dom/button {:onClick (fn [] (js/console.log "blah")} "Foo")

exit215:09:06

for example..

tony.kay15:09:19

that one should not be affected

tony.kay15:09:11

and I do advanced builds regularly.

exit215:09:49

yeah same here, very odd behavior for sure

tony.kay15:09:00

I would assert that the real example of a broken one is not that code

tony.kay15:09:21

@maxt what part of that 8ms is actually the DOM calls vs React overhead?

tony.kay15:09:36

Thomas Heller had done some performance tests a while back and the macro versions of Fulcro (which result in raw react calls) were about as fast as they could be…but 8ms for 100 li sounds large to me

tony.kay15:09:59

unless that is an initial DOM render…in which case most of that is probably actual DOM manipulation instead of just VDOM stuff

tony.kay15:09:35

wrapping a time around a call that makes 10,000 (or more) of those 3+ times and averaging might be a better measure of the Fulcro overhead

tony.kay15:09:42

Running 3x in a REPL, I mean, not in a loop

tony.kay16:09:42

better to put the time/loop in a function so that the js VM can optimize it

tony.kay16:09:02

you might see it get faster over multiple calls

maxt16:09:49

Yeah I wouldn't use those number to judge the performance overhead of fulcro, those where just for comparing dom and ldom. But of the 8 ms, about 1 is commit, 3 is spend in something called mark. render_Person takes about 2.5 ms

maxt16:09:15

This is also all development builds

tony.kay16:09:31

on my machine 10,000 divs take 14ms once warmed up

tony.kay16:09:49

what kind of build won’t affect that

tony.kay16:09:08

you’re testing the perf of a function that won’t be touched by adv compile

tony.kay16:09:44

I get the same time w or w/o the props map, but I’m using a string child…so the macro detection is getting it

tony.kay16:09:34

If I call createElement in that same loop, on my machine, 10,000 divs take 12.3 ms

tony.kay16:09:18

so about a 10% difference…and actually now that I re-run the Fulcro example, I’m measuring more like 12.5 to 13.5…so within the realm of statistical error

exit216:09:22

(dom/button
       :.btn.btn-link
       {:onClick (fn [] (prim/transact! this
                                        `[(listing/add-additional-charge {:charge-type ~charge_type :inventory-id ~inventory-id :charge-id ~(prim/tempid)})
                                          :listing]))} "Add More")

exit216:09:38

@tony.kay Ok, this is the real one

exit216:09:00

Works locally, but once built stops firing :man-shrugging:

tony.kay16:09:17

and your li example takes about 180 microseconds (18ms to render 100 items 100 times / 100 times) when measured this way. Your string building adds overhead (str …)

tony.kay16:09:03

but not a ton…goes to 22ms with the str building (.22ms for each list of 100)

tony.kay16:09:05

the extra overhead, internally, is the fact the that children are being “forced” (expanded from a seq to a js array)

maxt16:09:10

If I repeat your example, i get around 20 ms, but it varies much, and I can't see any real difference between react and dom

tony.kay16:09:33

it will vary…it’s modern hardware…name of the game

tony.kay16:09:38

but it stabilizes some

tony.kay16:09:53

which is why you run it mulitple times…the early measurements are usually wildly off

tony.kay16:09:03

(because the VM hasn’t warmed up (optimized) the code yet)

maxt16:09:40

Yes, I'm not worried about that at all. The ldom does take 1300 ms however 🙂

tony.kay16:09:56

in this same test? Yeah, that is crap

maxt16:09:10

Yes, just replacing the require

tony.kay16:09:48

ok…I just wanted us to be speaking the same language on how to measure performance. The flame charts are a nice check…but isolated testing like this is much more accurate.

tony.kay16:09:10

you can easily see stuff off by a factor of 10x

tony.kay16:09:47

general procedure: isolate, then put in function, run a few times until relatively stable, then run a few more and avg…you can be more “formal” about it (and there is a lib that does this called https://github.com/hugoduncan/criterium for the JVM), but the procedure I just outlined does well enough for pragmatic purposes.

tony.kay16:09:41

@njj The only thing I can think of is that perhaps your mutation add-additional-charge is somehow not getting required in your prod build

tony.kay16:09:47

remember that mutations are implemented with a multimethod, and if the multimethod does not run, it won’t be available to fulcro

exit216:09:09

defmethod mutate 'listing/add-additional-charge is how its defined

exit216:09:22

but I may try and move it into the UI as a test

tony.kay16:09:39

not asking how it is defined…more where, and if there is a direct require of that ns

tony.kay16:09:58

in dev mode you might be requiring it via alt dev paths/files

tony.kay16:09:03

so it would work there, but not in prod

tony.kay16:09:36

just getting the symbol right doesn’t get it installed…a require of that ns in the code does

tony.kay16:09:47

and if there is no require, there is no install

exit216:09:13

I moved it into the same file as the ui and redefined it as a defmutation in that file. Still nothing, so odd

exit217:09:21

@tony.kay I forgot to mention this is a subform, I added these fn as computed props and it worked

hoynk23:09:42

I am trying to use semantic-ui-wrapper but it seems events are not translated to clojure maps. How should I convert it ?