-
-
Notifications
You must be signed in to change notification settings - Fork 713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Testing for 0.5.0 release #125
Comments
summary: re: (yield) change: I see a nice improvement in my app's performance, but perhaps we can get an even better balance between the needs of the browser and the needs of the router. I'm writing an app that uses svg to allow the user to draw items onto a drawing surface. Currently I have one entry in the db when a new item is being created by a mouse drag and a separate entry for the list of items that already exist. In the "mouse up" handler for a creation drag, I dispatch two re-frame events: :end-drag, and :create-item. Before the yield change this gave a significant visual glitch as the drawing surface was redrawn on each yield: first without the new item (after :end-drag was handled) and then again with the new item in the existing items list (after :create-item was handled). In this case, the effect of the yielding to the browser before handling each event was a visual flicker ("there" "not there" "there") during item creation. The duration of "not there" is greatly reduced in my setup after the change to use (yield) instead of waiting for (timeout 0). Sweet! It does appear though that even with the new faster yield, software down the stack from re-frame (reagent/react/chrome) is rendering my svg every time the router loop yields. That seems like too much deference to UI handling over router event loop handling. I see in the comments that the purpose of the yield to make sure the browser gets some time in case the handler hogs the cpu for a long time. I suspect many handlers are extremely fast (manipulations of the db atom) and it seems to me we should try to handle as many as we can in some reasonably small time slice rather than yielding before every event. I tried this modification to the router yielding algorithm: when there is a new event to handle, check if it's been at least 5 ms since we most recently returned from a yield. If yes, yield now and start a new 5 ms timer when the yield returns. If no, handle the event without yielding first. This removed the visual glitch in my case and I think makes a better tradeoff between the needs of the browser and the needs of the router. Fast handlers will execute much faster on average and even if a slow handler slips in and starts at just under 5 ms after a yield returned, the effect on the responsiveness of the browser will be worse by at most 5 ms. Here's a sketch of some code that's working for me locally: (defn yield-chan
[flush? yield-time now]
(cond flush?
(do (flush) (timeout 20))
(>= now yield-time)
(yield)))
(defn router-loop
[]
(go-loop [yield-time 0]
(let [event-v (<! event-chan)
yield-time (if-let [ch (yield-chan (:flush-dom (meta event-v))
yield-time
(system-time))]
(do (<! ch)
(+ (system-time) 5))
yield-time)]
(try
(handle event-v)
(catch js/Object e
(do
(purge-chan)
(router-loop)
(throw e))))
(recur yield-time)))) |
@scgilardi I've been thinking about this and have an approach that we want to try involving running handler functions immediately. Do you have a public example of your SVG app we can use as a test case? Don't worry if you don't have it available, it's not critical. |
I updated and my app works as usual. It's a drop-in upgrade so far. Good stuff! |
@danielcompton Thanks! I extracted a public example out here: https://github.com/scgilardi/re-frame-svg-demo . It lets you draw rectangles and demonstrates the flicker I'm talking about. |
Updated my app and didn't have any issues. Good work! I will try to play with dynamic subscriptions in the next few days and see how it goes. |
No issues as well. Just less noise in the JS console! Thanks :) |
@scgilardi there's a new version of the router loop in I've dumped using core.async completely. Each processing cycle, ALL events in the queue are processed without yielding. But no NEW events are processed. They will be done next cycle. This seems to strike the right balance between draining the queue ASAP, and being a good CPU citizen. Obviously this is experimental but I have high expectations. It is not deployed to clojars yet (next couple of days). In the meantime, to use it, you'll have to:
|
There's a new event processing (router) loop. I replaced use of core.async with a hand-rolled solution. As a result:
I doubt this will affect normal apps. But it could affect games which depend on existing timings. Maybe. It could affect apps which dispatch large volumes of events (telemetry?) very quickly. Maybe. I'm not expecting any problems, just being a little paranoid. |
@mike-thompson-day8 thanks, it looks like a nice improvement. We can now think in terms of a packet of events being handled without yield. I updated my demo at https://github.com/scgilardi/re-frame-svg-demo and it's beautifully flicker-free after being made compatible with the new router policy. Nicely done! I have a few thoughts based on reviewing the code:
|
@scgilardi Thanks! I greatly appreciate that thoughtful review. You are 100% right about the bug. Thanks! Also, I've adopted your name changes. Good suggestions. I'll release an alpha3. Regarding the handling of events post an exception, it seems to me there is no strategy which will work generally, so sticking with something utterly simple seems right. For example, what happens if W (from a websocket) gets interleaved with your C D. Mind you, I have a selfish angle here in not looking too hard for solutions. In our apps, when an Exception is thrown, we do an "undo" to put |
proposed updates to new router, re: issue #125
@mike-thompson-day8 you're welcome! I'm glad it was helpful. Another quick one: The |
see alpha3 about to appear in clojars |
v0.5.0 now released. |
Hi folks
We've released https://github.com/Day8/re-frame/releases/tag/v0.5.0-alpha1. Try it out in your apps and let us know what you think. There are two changes that need particular attention:
(core.async/timeout 0)
to(yield)
in the router dispatch loop - Use yield instead of (timeout 0) #121. We need to give control back to the browser while we're processing for it to do Browser Things. We previously used(timeout 0)
to do this. The time taken for the(timeout 0)
channel to close is browser specific, but in general takes at least 4 ms. This meant we couldn't process more than 250 events/second. The newyield
implementation, helpfully shared by https://github.com/pkobrien is much faster. Again, we think this is implemented correctly, but let us know if you see anything funky.Thanks!
The text was updated successfully, but these errors were encountered: