Multithreading?
Should we allow component state to be changed from a background thread? Should it do realization, layout, or diffing on a background thread?
I lean towards "no" for all of that, for simplicity's sake.
From everything I know about React. If the codebase is written well enough, and Few seems to be, multi-threading should be fairly trivial to implement, for the diffing part.
Plus in Swift, there are GREAT tools for multithreading.
I'm not sure anything involving multithreading is ever trivial :smile:
I'm not convinced that the tradeoffs in complexity are worth it. I think we'd need some concrete cases where performance suffers to motivate this.
@joshaber I just saw Few today, so I don't know how complicated it to implement. I have a lot of experience with React. With React, Pete Hunt was able to trivially move all the React code to a different thread. The only problem with that approach was that preventDefault couldn't be called on UI events. From my understanding the same issue doesn't exist on iOS, as nothing has a default action to begin with.
Now perhaps, it is not important enough, and it is much more complicated to actually implement in Swift native code. But, I think that for larger apps, performance will become a bottleneck eventually, and multithreading could avoid that problem entirely.
Few is amazing, and very promising. I think it should reach for the maximum potential. Perhaps, its too early to work on a power feature like multithreading, but don't write it off for the future!
Note also that multithreading is not always a simple gain in performance. Sometimes the overhead involved in concurrency (and especially synchronization) can dominate the program, to where it's slower than if it were single-threaded.
(Just for context, when I last talked to some of the folks working on Components one of the big motivations for multithreading was text layout with non-English languages.)
Threading is hard. The interesting thing is that React Native and ComponentKit have two totally different approaches to multithreading:
- React Native has a single JS thread that is distinct from the app's main thread. However all JS operations must occur on this thread, so user interaction can be blocked waiting for JS to execute (e.g. state or model updates). The React Native guys are working on finessing this so that you can move certain expensive JS operations onto a separate thread.
- ComponentKit uses totally immutable component instances. This means it's trivial for us to move component construction off the main thread and parallelize it, but we can't do scoped subtree updates—that would require that we be able to mutate the component tree after creation. Any state change anywhere in the tree requires a complete re-render, which is pretty expensive and limiting.
After we open source these two I would love to be part of the conversation about what Few should do for threading.
@adamjernst Very interesting insights.
I have personal experience with React.js. In that case, all the JS operation do run on a single javascript thread, but as javascript is extremely async, the UI is never actually blocked. All the state and model updates in React are now async as well.
I don't know about componentKit. But you said Any state change anywhere in the tree requires a complete re-render, which is pretty expensive and limiting.
In the case of React.js and the DOM in the browser, it turned out that a complete re-render in pure code was still much faster than mutating views at all. Perhaps the same is true for iOS. Some benchmarking will show this soon.
I said that multithreading is a worthy goal earlier. This has all been informed by my experience of React.js
@adamjernst If you had access to immutable data structures in ObjC++, with structural sharing, like those that exist in JS (not used in React currently either) and used those to represent your component tree, would you be able to update part of the component tree without a complete re-render?
@mrjjwright No. The two things that allow you to do scoped subtree updates:
- Component descriptors. Within
rendera component should be instantiating descriptors of what children it wants to have, not the actual child instances themselves. This allows the infra to step in and recycle existing children when possible. - Refs. If a component needs access to an instance of a child, it must get it through the infrastructure, not via a reference that it stored previously. This is what React calls "refs".
Yes React enforces those 2 rules well and from what I understood from Ari Grant’s talk so does Components. So I am still surprised that above you said that Components can’t currently handle scoped tree updates, but I am thinking it is because you can't mutate the component tree at all while with React they have a system appears immutable but is actually mutable via the internal ref system.