Giter VIP home page Giter VIP logo

Comments (16)

vjeux avatar vjeux commented on May 13, 2024

The very basic caching layer that I implemented gives a huge performance benefit for React Native workload.

  1. Most updates in React Native are small, when you click a button, touch something or animate there's only a handful of elements that are affected after the diff algorithm.
  2. We keep everything in a single layout tree, all the previous screens, all the ListView elements... Recomputing the layout for all that takes a non trivial amount of time.

By having the caching layer, we keep small updates super fast.

As another anecdote, right now ComponentKit on iOS doesn't have caching implemented and cannot do 60fps animations because layout is now their bottleneck.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

Thanks @vjeux for the lighting fast response as usual. :) Interesting feedback. It looks layout is recomputed everytime because node->isDirty is always true. How does your caching layer work at a high level? I also am designing this from the start to compute layout off the main thread and to not create unnecessary views that just do layout.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

Feel free to just point me to a file name in React Native and I will figure it out.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

I am leaning towards keeping a global layout map for all layout data like I do for component state and just keep track of all layout params as they change. Then pass this state off to a stateless Layout.c backed Layout object that recomputes only portions of the layout tree that have changed.

from yoga.

vjeux avatar vjeux commented on May 13, 2024

Every time we update any property that impacts layout, we dirty the node and all the parents up to the root. https://github.com/facebook/react-native/blob/72d3d724a3a0c6bc46981efd0dad8f7f61121a47/React/Views/RCTShadowView.m#L271

Then, the next time we recompute layout, we start from the root and bail out super fast on all but the paths that have been dirtied. This general dirty strategy is really powerful, this is how we implement nested text, propagation of the background color and we want to use it for more in the future.

One optimization that we can do and should be safe is instead of going all the way up to the root, stop at any element that's position: absolute

from yoga.

vjeux avatar vjeux commented on May 13, 2024

that recomputes only portions of the layout tree that have changed

The big difference between React component model and layout is that a React component CANNOT affect the state of the parent, but layout can.

Let me give you a super dumb example. You have a container with two children stacked vertically. If the first child updates and becomes taller, then you need to change 1) the parent dimension and 2) the second child position.

You have two options:

  1. You can implement an algorithm that's going to try to track down the impact of all the updates and figure out the affected elements, but that's really hard to write and to get right.

  2. You can just restart from the top and use a caching strategy that's going to bail out for things you know for sure cannot have changed.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

Oh great point, you just saved me from going down a bad path, that makes very good sense. Holy cow, thanks so much! Closing and off to some simple dirty checking .

from yoga.

vjeux avatar vjeux commented on May 13, 2024

It took me almost a year to realize that! I've been trying to fit layout inside of the React model so many times and didn't understand why it wasn't working.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

So I decided to try to actually just use React Native's UIKit layer to drive my views. Hold your breath :) I want try to actually use React Native's native layer with my component layer since the React side already has so much work put into it and handles layout, updating of the views and everything else so well. From my component side I can pass NSArray, NSDictionary to the React side quite easily to mimic the necessary JSON. I don't want to use React as the component layer because I am experimenting with something I can't talk about quite yet. In 1 hour of work I am fairly close. :) I wrote my own RCTJavaScriptExecutor implementation to pretend like I am on the other side of the JS connection even though my app is running local. It's all working great in my own "javascript" thread. The only question is the protocol over the bridge going to involve anything crazy that you should warn me about right now? I am trying to reverse engineer it right now. Also are the Facebook police driving to Denver to arrest me right now?

from yoga.

vjeux avatar vjeux commented on May 13, 2024

I wrote my own RCTJavaScriptExecutor implementation to pretend like I am on the other side of the JS connection even though my app is running local

Nice! At some point in the future we want to document that protocol so that we can have other abstractions on-top like Angular Native, Ember Native... If you conform to it, then you can use all the iOS and Android modules and API that have already been bridged.

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

Cool so the approach I am using is looking through BatchedBridge, MessageQueue and of course looking at actually stuff sent from AwesomeProject. I should be able to figure it out.

On Apr 27, 2015, at 2:37 PM, Christopher Chedeau [email protected] wrote:

I wrote my own RCTJavaScriptExecutor implementation to pretend like I am on the other side of the JS connection even though my app is running local

Nice! At some point in the future we want to document that protocol so that we can have other abstractions on-top like Angular Native, Ember Native... If you conform to it, then you can use all the iOS and Android modules and API that have already been bridged.


Reply to this email directly or view it on GitHub #73 (comment).

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

It's working!

On Apr 27, 2015, at 2:37 PM, Christopher Chedeau [email protected] wrote:

I wrote my own RCTJavaScriptExecutor implementation to pretend like I am on the other side of the JS connection even though my app is running local

Nice! At some point in the future we want to document that protocol so that we can have other abstractions on-top like Angular Native, Ember Native... If you conform to it, then you can use all the iOS and Android modules and API that have already been bridged.


Reply to this email directly or view it on GitHub.

from yoga.

matt-d-rat avatar matt-d-rat commented on May 13, 2024

...and it was at this point the Facebook Police busted down @mrjjwright door and we never heard from him again ;-).

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

Still alive here. :) I have a conceptual follow up question to this. I have succeeded in building a nice off the main thread layout system that uses a CADisplayLink (actually CVDisplayLink on Mac) to sync with the main thread and render.

However, if a root view (let's say a React Native root view or some other ui view hosting a layout system) has it bounds changed, obviously the layout needs to change. If the layout happens in a background thread, how to sync the changes visually with the root view? The root view will have it's frame/bounds change and by the time you calculate the layout on a background thread and get back to the main thread the user will see a jump visually. It's not that layout takes a long time, it's just that there is a break in the visual main thread transaction by switching to another thread.

I am seeing this when responding to AppKit NSWindow/NSView frame changed notifications by triggering layout, (there are visual gaps as the layout catches up with the user resizing the window). When I do everything on the main thread there is no jump. I looked through the React Native code and I didn't see any code that does any Core Animation transactions or anything like to make sure things are visually synced.

I guess having layout on a background thread works well for things that don't need immediate visual response but otherwise (for things like interactive animations) layout needs to be calculated immediately on the main thread? (Followup: this seems to be nicely explained here by the AsyncDisplayKit team. http://asyncdisplaykit.org/guide/4/. )

from yoga.

vjeux avatar vjeux commented on May 13, 2024

You are spot on, we're facing this exact issue and haven't yet figured out the right solution to fix that :)

from yoga.

mrjjwright avatar mrjjwright commented on May 13, 2024

I am thinking that designing for "async" layout first is good but a bit naive. In general a well designed UI rendering/layout system will be responsive on the main thread and give the user as much immediate visual feedback as possible. There is an obvious limit there (the each tick should take roughly 12ms limit) that can happen with text calculation or any number of things that take a long time, so the system must offload work asynchronously and bring the result of that work smoothly back in with the parallel work that has been occurring on the main thread for the user. In order for this to happen you have be able to reason about the animation, layout and measurement work along these lines.

I am wondering if this same sort of reasoning ability should be a first class citizen in a well designed component system as well. The component author needs to be able to reason which of their components need to be immediately responsive and which can have work (and it's associated props and state) offloaded to a background thread and then have the results smoothly brought in with main thread work.

from yoga.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.