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-i...[1] http://simonmar.github.io/bib/papers/strategies.pdf
[2] http://hackage.haskell.org/package/parallel-3.2.2.0/docs/Con...
sub fib(Int $n) {
return $n if $n < 2;
my $t = start { fib($n - 2) }
fib($n - 1) + await($t);
}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.
Haskell didn’t have it originally. Ok. But what do you think that says about whether it’s a new idea or not? What’s the connection? Why mention it in this thread about whether it's a new idea or not?
> The only systems which have supported compute-oriented composable parallelism like this are Cilk and TBB
Because it isn’t true. Haskell is an example of why it isn’t true.
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.
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.)
Hyperthreading and out of order execution are meant to accomplish keeping the execution units busy.
Now NodeJS or Python Asio style concurrency _are_ truly limited by their VMs and can’t do native parallel computations in a single process.
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).
Related work in Common Lisp:
https://github.com/pcostanza/claws/
https://european-lisp-symposium.org/static/2014/costanza.pdf
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.
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).
Whereas in Julia, we use Cthulhu to read code ;) - https://github.com/JuliaDebug/Cthulhu.jl.
OpenMP, however, depends on not just compiler extensions, but pragmas, so it tends to not find traction outside of the HPC world.
I think it has been highly influential though.
What? Lots of parallelism doesn't use message passing!
Hence, in order for that up-front cost to be worth it, the algorithms run on the GPU need to re-use that data many times. If the algorithms running on the GPU don't make extensive reuse of the data sent to it, it would be faster to just do the calculation on the host CPU.
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.
What is 100% new and does not exist in any other system is that the task scheduling is depth first (rather than breadth first), which is a much better order for doing compute-heavy workloads. This ability is new and was the culmination of an Intel research project, applied to Julia.
Proof of optimality was in the early cilk papers in the '90s.
I'm sure the same algorithms made into Julia via Intel (which bought cilk in the early '00), but it is hardly new except in the 'everything old is new again'.
Also IIRC OpenMP supports (or at least allows) work stealing although at least GCC does not implement it yet.
https://www.cs.cmu.edu/%7Echensm/papers/ConstructiveCacheSha...
See slide 8 here: https://www.microsoft.com/en-us/research/wp-content/uploads/...
Later versions of Blelloch's language are referred to in other parallel Haskell papers.
FWIW I think this is nonetheless fantastic news for Julia! It's certainly poised to do great work in areas that I doubt will get any Haskell traction anytime soon (if ever) for good reasons.
EDIT: Interesting, I think Julia is taking a coarser grained approach here that breaks down a bit for irregularly balanced execution trees, but relies less on fusion (i.e. doesn't use fusion at all) which is nice. I'm curious to see how this particular implementation of the "depth first" idea goes!
EDIT EDIT: Actually I'm not sure how much fusion is necessary for GHC's approach... Time to investigate more closely. Also call out to anyone more familiar with Data Parallel Haskell to chime in. I'm getting out of my depth.
> "async/await in C#, which is concurrent but not parallel"
C# is multithreaded. Things can only be parallel if you have 2 separate paths of execution that don't depend on each other or any serial order, but you can also kick off millions of individual tasks in a single function and await them all too. These tasks are all scheduled onto threads in a managed threadpool that also share work and run at the same time. I fail to see how this is any different or less capable than the others you mentioned.
https://stackoverflow.com/questions/37419572/if-async-await-...
https://blog.stephencleary.com/2013/11/there-is-no-thread.ht...
https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-a...
I said that C# also uses Tasks which are scheduled onto these threads, which is what you said Julia does, so it's the same. C# can also start new threads just as easily, or use channels like Go with the TPL/Dataflow, System.Threading.Channels, actor frameworks, Reactive Extensions, or just chaining tasks together. Other than the depth-first algorithm, it seems like C# has the same capabilities as Julia and the others, if not more, and is much more "mainstream".
Can you explain how blocking functions are automatically yielded without keywords though? I cant find any documentation on this that doesnt mention the @async macro.
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.
C# code that uses Tasks and C# programs built on Dataflow are very different. The latter benefits you most when you commit entirely to that paradigm.
If you want to get more low-level, low-overhead (for i.e. simulations), the same thing applies to things like Threading Building Blocks[1] in C++. I'd even argue that TBB is one of the best applications of this theory, going so far as to ship with dedicated tooling for the whole lifecycle of designing to implementing to profiling task- and graph-parallel programs [2]. Furthermore, it works well with other HPC libraries. Here [3] is an example of mixing it with OpenMP.
In summary, both Dataflow and TBB (et al) can readily be used to create CPU-intensive, proactive computing pipelines today. Even though they're "just" fancy Task schedulers, which are in turn "just" fancy thread multiplexers.
[1] - https://www.threadingbuildingblocks.org/tutorial-intel-tbb-f...
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.
Fair or not, Julia does seem to have the potential to be the first really interesting shift in this area in ages. Doesn’t mean it will get there, but there is interest.
What do you mean by impact/shift exactly? I guess I'm not understanding what's suddenly going to change now since, as you say, these ideas are old and implemented several times before.
Julia, for good or ill, seems to have some potential to be.
Anyway, if by data parallelism you mean simd, then that is already available and widely used in Julia. Multithreading comes on top of that.
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___re...
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".
Rust, while perhaps not as mainstream and not necessarily targeting scientific computing users, has data-bound parallelization like this via the rayon library.
I think there are a few more of those languages. AFAIK Clojure and Rust should be added to the list.
Concurrency? Go is not good at parallelism, compared to, say, Haskell.
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!
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.
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.