16 ms·
Concurrent JavaScript: It can work
- rmrfrmrf 9y agoForgive the ignorance, but why do we need threads when we already have the event loop? And, as a follow-up, why introduce a new concurrency model that's based on Java instead of, say, creating a concurrent "event pool" where execution order isn't guaranteed and callbacks are executed with maximum concurrency (edit: to clarify, I mean maximum parallelism)?
- akmittal 9y agoI think eventloop implements concurrency only but thread will implement concurrency with parallelism.
- rmrfrmrf 9y agoI edited my comment to clarify (hopefully). What I was thinking was that a theoretical "task pool" would execute callbacks with maximum parallelism.
- STRML 9y agoEvent loops are great for handling work that can be handed off occasionally to worker threads (like how Node works with libuv), but are awful when doing consistent CPU-bound work, such as with games, 2D/3D visualizations, finance, and the like. SharedArrayBuffer is a nice primitive that allows multiple Threads (or WebWorkers) to work on data in parallel. The additional primitives they're proposing offer quite a bit of flexibility. For one, I would really like an easy `new Thread(fn).asyncJoin()` to create a Promise to do expensive CPU-bound work. There are a few npm modules that do some version of this but it can be slow. As for the concurrency model, it should be possible to delegate tasks to a pool in this one. Map an array of input data and functions to an array of promises, then Promise.all() or Promise.race() on them.
- pitaj 9y agoI think that kind of stuff it much more relevant in the land of WebAssembly. It certainly makes sense there, but in normal JS, I doubt the usefulness.
- STRML 9y agoIt depends. It could be very useful for React/React Native, for one.
- rmrfrmrf 9y agoIf you're that concerned about performance, though, aren't doing things like `.asyncJoin()` just going to eat up any potential gains by 1) wrapping the result in a Promise and 2) throwing the Promise back into the event/microtask queue? It seems like the transport would quickly become the bottleneck. I'm also struggling to understand what tier of performance you're aiming for, since you're hoping to boost performance by using a Thread in JS when, in node, you can already code expensive computation in C++ and expose a binding back to JS (which to me makes more sense in the context of game design since that's basically what most already do with Lua--this would just be in reverse).
- lylepstein 9y agoJoins like that wouldn't really be used for coordinating between multiple threads in a fine-grained fashion. For example, maybe you'd kick off two worker thread from your main thread, and each of those is going to do some fine-grained processing on some shared set of data. The main thread needs to know when that process is finished, so it joins both threads.
- spankalee 9y agoIt's awesome to see work on concurrency, but I'd really like to se a model that didn't involve shared memory. JavaScript on the web already has the concept of Transferrables. It'd be great to explore how user-land code could create transferrable objects and graphs of them so they could be sent between threads without threads sharing the same heap.
- amelius 9y agoSorry, but we need shared memory for fast sharing of large immutable data structures.
- Jtsummers 9y agoShared memory could be hidden from the users. Passing objects would really be passing control of objects. If process A creates object a, and passes it to process B, no copying has to happen if the underlying mechanism uses shared memory and access control to effect the transfer (a change of owner).
- spankalee 9y agoYes. Presumably transferrable objects would have to be created on a shared heap. Non-transferrables would live on thread-local heaps.
- pizlonator 9y agoI find that thinking about multiple heaps is harder than thinking about threads. (Ever programmed with scoped memory? It's a nightmare.) I find that thinking about transferrables is harder than thinking about threads. It's gross that an object suddenly loses all of its state just to give that object to another thread. Then, other parts of your code that still want access to that data have to do things that are definitely more gross than acquiring a lock.
- amelius 9y ago
- LeoNatan25 9y agoThis is very interesting. If this is indeed implemented, it will remove a serious hurdle from the equation of RN. Still JS, but the ceiling will go up considerably.
- Animats 9y agoOf course the language can be made to support it. Given the way Javascript is used, the question is whether supporting it will create a big mess. They have some good ideas. One is that variables can be marked as restricted to one thread. That should be the default. Other languages could benefit from that feature. Python, for example. If you want to get rid of the Global Interpreter Lock, knowing which variables can't be shared between threads is a big help. Variables should be owned by a thread or owned by a lock. Rust went that way, and it works well. The proposal here is old C-style locking, where there are locks and variables, but the language doesn't know which locks are protecting which variables.
- pizlonator 9y agoI think it would be weird if objects were thread-restricted by default. Concurrency is most fun when it's easy for a thread to access an object it wants.
- richardwhiuk 9y agoYou say fun, but what you mean is twisted mess.
- pizlonator 9y agoWebKit uses threads. WebKit is not a twisted mess.
- coldtea 9y agoWebKit needs tons of extra effort to keep it from being a twisted mess precisely because it uses threads.
- pizlonator 9y agoCan you be very specific?
- zengid 9y agoGenuine question: Aren't locks a quagmire in terms of lock management? Why start there?
- pizlonator 9y agoAll of the alternatives are an even bigger quagmire.
- preek 9y agoJS already has a proper concurrency model which is the event loop. What it doesn't have is a model for parallelism. It's important to understand the difference! It can be hard to grasp at first, because few people make the proper distinction. If you're new to the subject, don't take my word on it, but watch the talk "Concurrency is not parallelism" by Rob Pike[0]. It's highly recommended. Learning a language that makes a proper distinction will help - I got it through Clojure, but there's certainly others like (obviously) Golang. 0. https://youtu.be/cN_DpYBzKso https://youtu.be/cN_DpYBzKso
- pizlonator 9y agoEven loop is not concurrency. Just because concurrency and parallelism are different does not meant that concurrency and event loops are the same.
- christophilus 9y agoYou can build concurrency on the event-loop, though, which is what the OP was probably referring to.
- pizlonator 9y agoNot quite. Consider a component in your code that does this: for (let blah of things) foo(blah); In an event loop, if things has a lot of stuff in it, this can block the event loop. Other things in the event loop won't be able to run until this completes. With threads, this just works.
- enoah_netzach 9y agoThat is parallelism, not concurrency. Concurrency: two functions can run one after the other while they are non blocking for the callers (e.g. hyperthreading). Parallelism: two functions can run at the same time (e.g. multiple processors).
- 9y ago
- ender7 9y agoPlease, no. Shared-by-default heap memory is one of the greatest mistakes ever made in language design. We need a better design justification than it's the easiest thing to implement right now.
- imtringued 9y agoThe justification is that when you have an array of 100000 elements and split it into into isolated chunks that can be processed without any synchronisation and then marshall it into json and then actually do the computation and then marshall the result into a json string and marshall it back into a javascript array is very wasteful. Heck even when I'm passing immutable messages I'd still implement it via shared memory to avoid the wasteful marshalling step.
- spankalee 9y agoFork/join on arrays and trees would be an easier to manage model, IMO.
- pizlonator 9y agoYou can implement that on top of threads.
- spankalee 9y agoOf course you can. You can implement anything on top of threads, especially non-determinism, race conditions and deadlocks.
- flagxor 9y agoEvent loops have these too, they just go by different names: * non-determinism -> order in which setTimeouts run * race conditions -> callbacks happen in an unexpected order * deadlock -> broken callback chain
- einrealist 9y agoYeah, and lets have the gazillion scripts, loaded by your usual favourite news site, spawn 10 threads each... For browsers, this is a bad idea. The biggest advantage of the event loop approach is that it is somewhat deterministic. With threads, that determinism goes out of the window. Lock-management? No way I do that for Browsers. I will quit working on client-side code if that is becoming a requirement! Threads for the server-side? I don't care.
- rmrfrmrf 9y agoFor that matter, anyone who'd like to have "fun" with concurrency and lock management in browser-side JS is free to play around with recurring asynchronous data fetching cached in localStorage when multiple browser tabs are open. Spoiler: locks are the wrong approach and you should be relying on events/callbacks instead. Oddly enough, that's similar to my gut feeling about Threads in JS.
- einrealist 9y agoA browser can easily optimise HTTP or localStorage calls internally, without introducing Threads to the "userland" code.
- rmrfrmrf 9y agoI'm talking about a userland implementation.
- blockoperation 9y agoWhether it's a good addition to the language or not is debatable, but as a browser feature, it sounds terrifying. Browsers have a huge attack surface as it is (I mean, WebGL is a thing – exposing GPU drivers to random untrusted code on the internet, what a brilliant idea...), and exposing threading will only make it worse. Every single API would have to be carefully audited and made thread-safe, and I'm sure that many 'fun' bugs would crop up as a result.
- 9y ago
- rkeene2 9y agoJavaScript should just copy the Tcl threading model.
- bzbarsky 9y agoWhat's not quite clear to me is whether "the cell never moves" is one of the assumptions that actually goes into making this work or not. And if it is, how that plays with a compacting or generational collector, where you do in fact want the cell to move.
- pizlonator 9y agoFrom the standpoint of the concurrency scheme I'm proposing, it's OK to move the cell in the GC. Then it becomes a standard GC moving problem. In JSC we don't move cells for other reasons, and the point of saying that they don't move is that the object model itself does not require the cell to ever move.
- bzbarsky 9y agoThank you for the explanation!
- throwaway613834 9y agoThey mean parallelism, right? Concurrency is what JS is already known for.
- zzzcpan 9y agoYeah, JS definitely has concurrency. He's just twisting the term a bit into his world of shared-memory multithreading to make his model sound superior to other concurrency models.
- markc 9y agoOr use ClojureScript with core.async - which implements a go-like concurrency model. (Not saying that's fully or equivalent, or meant to denigrate this interesting work.)
- iainmerrick 9y agoI feel like most of the comments here are ignoring the fact that this is a straw man proposal, as the blog post emphasises several times. They aren't proposing to add threads to JS in exactly this manner. They're pointing out that it would be technically feasible, for WebKit at least, and wouldn't add any unnecessary overhead. The specific proposal is just a starting point for discussion.
- BatFastard 9y agoOne other thing to consider, the SharedArrayBuffer can only carry limited data types, it's not a shared object. I used Flash's SharedArrayBuffer and found it was very limited in what you could use it for.
- zackmorris 9y agoI'm late to the party but please for anyone reading this, look into the actor model. Shared mutable state quickly becomes unmaintainanble. It used to be slow to do shared immutable state but that's no longer the case. Today there are many better ways to do shared memory with copy-on-write so data is only copied if it's changed. Think back on history - there are only a handful of computation approaches that have stood the test of time. Pipes in unix, spreadsheets, data management systems like MS Access or FileMaker. Had concepts from those programs made it into programming, our lives would be a lot simpler today. We don't need to torture ourselves for imagined efficiency by copying the C threading model that quickly dissolves into spaghetti.
- hurricaneSlider 9y agoHi Zach, I'm working on a implementation of the actor model for node, and since you mentioned it, I was wondering if you would perhaps be able to provide some feedback on it. Still very early days, but making some good progress on a single node implementation.
- zackmorris 9y agoYa sure, I could maybe take a look. I have to be honest though that I haven't used the actor model for a project yet. I just get a lot of leverage from its principles when I do shell scripting. In Node I would be concerned about shared mutable memory. Just curious how you are isolating the actors? Or is that still in the todo stage?
- porker 9y ago> there are only a handful of computation approaches that have stood the test of time. Pipes in unix, spreadsheets, data management systems like MS Access or FileMaker. Had concepts from those programs made it into programming, our lives would be a lot simpler today. Can you go into more detail on which concepts from Pipes in unix, spreadsheets, data management systems like MS Access or FileMaker you're thinking of? My familiarity with the tools is leading to blindness about application...
- first_amendment 9y agoJavaScript is already concurrent. It isn't parallel though. Is there any real world application that currently cannot exist without this feature? It seems like a solution looking for a problem. I can see needing the ability for parallel JS for highly-parallel compute-heavy applications but those already seem serviced by SharedArrayBuffer. So... why?