13 ms·
It's hard to overstate what a big deal this is (and how much effort went into it). The only mainstream languages with this kind of composable parallelism are Go
by StefanKarpinski 7y ago
It's hard to overstate what a big deal this is (and how much effort went into it). The only mainstream languages with this kind of composable parallelism are Go and Erlang, both of which target writing servers, rather than high-performance computing, and which therefore optimize for I/O throughput rather than CPU throughput.
The only systems which have supported compute-oriented composable parallelism like this are Cilk and TBB, which are both really excellent and successful—and provided major inspiration for this work. However, they both seem to have fallen short of widespread adoption partly due to being non-free (until recently) and also, I suspect, because they are non-standard C/C++ language/compiler extensions, which seem to never catch on that broadly.
This is a brave new world for high-performance technical computing, not just for Julia.
- eigenspace 7y agoAs a physicist, it's been really cool seeing the awareness of Julia in the community gradually increase. Large swathes of the physics community have been migrating their code to Python over the past few years but it really seems now that Julia is getting so many amazing features and packages that it can't be ignored.
- benjaminva 7y agoSame here, I worked in a computational biophysics department with lots of python/bash/R and I was the only one who wrote lots of high-performance code in Julia. People were curious about the language but it was still very much unknown. Hope we will see a broader adoption of Julia in the future - it's just that much better for the stuff we do on a daily basis.
- Arnavion 7y ago>The only mainstream languages with this kind of composable parallelism are Go and Erlang, both of which target writing servers, rather than high-performance computing, and which therefore optimize for I/O throughput rather than CPU throughput. Rust, while perhaps not as mainstream and not necessarily targeting scientific computing users, has data-bound parallelization like this via the rayon library.
- oconnor663 7y agoWhich indeed was named after Cilk.
- jakear 7y agoThe interface does look a lot like Cilk, which is a definite plus as that is an absolute pleasure to use. This however offers a lot more configurability than Cilk (or at least more accessible configuration). Whether that’s a curse or blessing... I guess we’ll find out! :) Also, have you looked into implementing Cilk-style reducers at all? [0] [0]: https://www.cilkplus.org/docs/doxygen/include-dir/group___reducers.html https://www.cilkplus.org/docs/doxygen/include-dir/group___re...
- leiroigh 7y agoBig difference is that Cilk appears to typically synchronize on function return, while julia needs an explicit wait/fetch (there is also a @sync macro). That is, spawn/wait can be matched from the AST in cilk, while julia demands that the user communicates and stores a reference to the started task for later waiting. It is easy to forget to wait on a task, or even lose reference to it. The current scheduler will typically execute this task quickly, even if nobody ever waits on it; until load changes and you have races everywhere. There are currently no warnings comparable to Python's "RuntimeWarning: coroutine foo was never awaited".
- jakear 7y agoCilk does indeed sync on function return. You can also manually call 'cilk_sync'. I do think that Cilk’s approach is better (more in line with “make error states unrepresentable”, which I’m a big fan of), but Julia’s approach seems to be more in line with their overall goal of giving the programmer as much control as possible. Not sure if that’s a good thing or not, but as said above... we’ll find out soon!
- gpderetta 7y agoAffine types to represent futures would help here.
- conjectures 7y agoGreat to see this finally land - thanks for all the team's work. Looking forward to giving it a whirl. Threading is something of a prerequisite for acceptance as a serious language among many folks. So great to not just check that box, but to stick the pen right through it. The devil is always in the details, but from the doc the interface looks pretty nice (given threaded code generally looks like it was written by Cthulhu).
- KenoFischer 7y ago> given threaded code generally looks like it was written by Cthulhu Whereas in Julia, we use Cthulhu to read code ;) - https://github.com/JuliaDebug/Cthulhu.jl https://github.com/JuliaDebug/Cthulhu.jl.
- thekhatribharat 7y ago> this kind of composable parallelism are Go and Erlang, ... which therefore optimize for I/O throughput rather than CPU throughput Parallelism is always for CPU throughput, precisely why you don't run any more OS threads than the number of cores. Concurrency primitives (whether in Go, Erlang or any other programming language) are meant for I/O-bound tasks. Let's not conflate concurrency primitives enabling I/O-bound tasks and parallelism primitives enabling CPU-bound tasks.
- elcritch 7y agoYou can still use Erlang style concurrency (or Go style as well) to truly parallelize CPU bound tasks. It’s a bit less efficient due to Erlangs design where you can’t readily share mutable data between processes but will still enable true parallel calculations on all cores. By default BEAM runs one execution thread per core. For example, it’d be trivial to write a true parallel merge sort in Erlang/Elixir. Now NodeJS or Python Asio style concurrency _are_ truly limited by their VMs and can’t do native parallel computations in a single process.
- chrisseaton 7y ago> precisely why you don't run any more OS threads than the number of cores It’s often worth running more than your number of cores, in order to keep execution units busy during memory fetches for example, or integer units busy while float units are a bottleneck otherwise. (Did a PhD partially on this kind of thing.)
- BubRoss 7y agoThat's what hyperthreading is for, which implies having the same number of threads as logical cores. Hyperthreading and out of order execution are meant to accomplish keeping the execution units busy.
- chrisseaton 7y agoNo it implies having the same number of OS threads as hardware threads, which is more than the number of hardware cores.
- scott_s 7y agoDo people use Cilk heavily in the high-performance computing world? They did not when I was part of it. I think OpenMP (https://www.openmp.org/ https://www.openmp.org/) is what more people would reach for. It started as a way to parallelize loops, but has had a task framework for a long time which I believe also allows composeable parallelism. OpenMP, however, depends on not just compiler extensions, but pragmas, so it tends to not find traction outside of the HPC world.
- krapht 7y agoThe real gain is with MPI, anyway... shared memory is a trap. All the projects I've worked on have exploited parallelism through message passing, or a battle tested multithreaded linear algebra library.
- BubRoss 7y agoThere are many techniques and calling any of them the one solution and all others a trap is extremely myopic. Shared memory is extremely useful for IPC and fork join parallelism.
- vmchale 7y ago> All the projects I've worked on have exploited parallelism through message passing What? Lots of parallelism doesn't use message passing!
- seanmcdirmid 7y agoGPU is the way to go these days to get most parallelism, and it is definitely the shared memory variety. Message passing is the go to when parallelism needs to cross machine boundaries.
- steev 7y agoGPU parallelism is highly specialized and requires very specific workloads (e.g., many linear algebra workloads). GPUs are a bad choice for general parallelized tasks.
- mruts 7y agoScala futures seem to offer all of this functionality as well, no? And you can do all the regular monad stuff on them as well, which is super nice.
- manigandham 7y agoYou're missing C#/.NET which has had async/parallel programming constructs for a long time, and served as the model for await/async copied by many other languages. It's gone through several design patterns from the original APM (async programming model) to EAP (event based pattern) to TPL (task parallel library) to the all purpose Task/ValueTask embedded into the language. It's very fast and efficient, and you can build your own scheduler too. Combined with hardware intrinsics in the latest .NET Core, it's very good for computational and server work. I'm surprised Julia considers this such a big update but it does seem to be an overstatement to say this changes technical computing.
- opportune 7y agoI really dislike using Tasks over threads due to the reusability issues. Specifically that Tasks are not reusable and threads are. It makes parallel computational work a headache, because you need to implement the reusability in a higher scope than tasks, then make an effort to prevent race-conditions, double checkouts, etc. Tasks are really meant for responsive computing, not proactive computing (yes, even TPL and Dataflow), and thus I don't think they are well suited to parallelized computation in areas like e.g. simulation.
- manigandham 7y agoWhat do you mean by reusable exactly? And how does proactive computing differ? Do you have an example you can share?
- opportune 7y agoReusable as in, I can create a datastructure in a thread, do some computation, yield control from the thread (wait for something else to tell it to go again), then come back to the thread with the same datastructure and keep going. With Tasks, you are supposed to let tasks complete rather than let them pause/sync so anything you declare locally is lost when you want to restart it. The workaround is to declare things in a higher scope and pass them into tasks as they are reran - this makes tasks not suited to things where you want to persist data, since tasks are opinionated in that they are only meant to contain logic, not state, so you have to add your own external logic to manage the allocation of state to the tasks that you keep recreating This trait of tasks makes sense when writing things like web servers because it prevents developers from shooting themselves in the foot by writing something that isn't stateless. By proactive computing, I mean some program that always has control of the entire execution graph. For example, a simulation may initialize a bunch of datastructures, separate some of them into threads, run the threads, wait for all of them to complete x ticks, do some synchronization or measurement of the total system, then continue y times, and exit.
- pka 7y agoFor completeness' sake, Haskell has had this for over a decade :) A SO thread that gives a nice overview [0], the paper [1], the library [2]. Here's how a parallel fib looks like: fib :: Int -> Int fib n | n <= 1 = 1 | otherwise = x ‘par‘ (y ‘pseq‘ x + y + 1) where x = fib (n-1) y = fib (n-2) [0] https://stackoverflow.com/questions/958449/what-is-a-spark-in-haskell https://stackoverflow.com/questions/958449/what-is-a-spark-i... [1] http://simonmar.github.io/bib/papers/strategies.pdf http://simonmar.github.io/bib/papers/strategies.pdf [2] http://hackage.haskell.org/package/parallel-3.2.2.0/docs/Control-Parallel.html http://hackage.haskell.org/package/parallel-3.2.2.0/docs/Con...
- pkofod 7y agoFor complete completenes sake, Haskell has existed for almost three decades :)
- dwohnitmok 7y agoWhile the earliest versions of Haskell date back that far, those combinators only came out in 2007 (IIRC).
- chrisseaton 7y agoThose functions weren’t part of Haskell back then.
- pkofod 7y agoWhat do you mean? That's my point exactly. While Haskell has that now, they didn't always have it. If a programming language is created in 2019 they can certainly instantly incorporate insights from existing technology in their design an plans. However, that doesn't mean that any programming language/compiler project started today will instantly have all of those features. Things take time in each new framework.
- deleted 7y ago[deleted]
- phillipcarter 7y ago(Since this is basically now a comment where everyone "me too"s in a reply...) F# also has facilities for this, depending on what you're after. The _MailboxProcessor_ type, though not as "for real" as the green threading in Go, allows for some pretty high throughput with a relatively sane message-passing abstraction. And .NET itself offers many means to do parallel programming (which can get quite optimized), of which F# has some nice abstractions for. So I don't think it's quite fair to say that only Go and Erlang have good support for parallelism. I also don't think they use their abstractions typically for parallelism (but I good be wrong).
- nickpeterson 7y agoThat name though, couldn't it just be Agent?
- kgwgk 7y ago> (Since this is basically now a comment where everyone "me too"s in a reply...) Related work in Common Lisp: https://github.com/pcostanza/claws/ https://github.com/pcostanza/claws/ https://european-lisp-symposium.org/static/2014/costanza.pdf https://european-lisp-symposium.org/static/2014/costanza.pdf
- DNF2 7y agoHe didn't really say that, though, rather that just Go and Erlang has a similar kind of _composable_ parallelism. (Don't know about Haskell.)
- holy_city 7y agoMATLAB has had this for years. But it costs $1,000 on top of the existing license cost. And it's lumped together with their GPGPU/cluster tools.
- DNF2 7y agoIf you're talking about the Parallel Computing Toolbox (PCT), then, no, you do not get access to multithreading through that, only multiprocessing and GPU processing. I recently considered buying the PCT, and this is one of the main reasons I probably won't. The only type of multithreading in Matlab is the type that is built into the core language, and you don't get any control over that. Finally, the claim to originality here isn't just the introduction of multithreading (which isn't even new in Julia!), but the particular threading model and scheduler, which lends itself particularly well to composable multithreading.
- chrisseaton 7y agoThis is a bit over the top isn’t it? It’s just task parallelism. It’s exactly what for example Haskell has been doing for decades isn’t it? It’s even the same demo and interface!
- DNF2 7y agoMy understanding is that it's not just about the parallelism, but about the type of threading model that is used, which makes it extremely composable.
- tomp 7y agoIt's literally just futures. (Still very hard to implement, of course, and in particular IMO this kind of implementation (M-to-N threading) is much better than the async/await implementations) but CS is pretty settled on futures being the "best" model, even C++ has them!)
- DNF2 7y agoMy understanding was that the depth-first scheduling was basically 'new'.
- chrisseaton 7y agoIn my experience most work-stealing schedulers for task parallelism like this are going to be depth-first. I’ve written several myself. Primarily for temporal locality - if you just created a task it’ll be resident in cache.
- gpderetta 7y agoI'm still trying to access the relevant papers, but from bits of discussions here it seems to me that the difference between the Julia scheduler and cilk is that the former will try to distribute sibling leaf tasks among executors enforcing as close as a global depth first view as possible, while cilk does depth first on each executor but different executors will be exploring distinct the problem space branching off as close to the root as possible (due to the steal the oldest ancestor behaviour). It is a compromise between better cross executor cache locality and increased contention I guess.
- deleted 7y ago[deleted]
- vmchale 7y ago> The only mainstream languages with this kind of composable parallelism are Go and Erlang Concurrency? Go is not good at parallelism, compared to, say, Haskell.
- bakery2k 7y agoWhat's wrong with parallelism in Go?
- marmaduke 7y agoWill this be worked to scale to multiple machines?
- StefanKarpinski 7y agoJulia has supported distributed compute forever. Just give it a machine file and you have an instant fully connected cluster.
- StreamBright 7y ago> The only mainstream languages with this kind of composable parallelism are Go and Erlang I think there are a few more of those languages. AFAIK Clojure and Rust should be added to the list.
- radiospiel 7y agoI have something in the works which is very similar - but is language agnostic. Pro: no need to switch to another language; Con: not high performance. We are using this in production for now over 1 year, and it helps tremendously. We are currently only supporting a ruby client, but other languages should be more or less easy to bring into the mix. A wider release is planned for later this year - feel free to ask me anything via email!