Live data from Hacker News

Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

erlang-solutions.com

61–70 of 114 posts

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#61

> Programming with concurrency primitives is a difficult task because of the challenges created by its shared memory model. I never understood this often repeated point. As junior / mid-level developer I had the privilege to run self written .jar files on government scale systems with more than 50 cores. I used Java thread pools and concurrent data structures to do heavy cross thread caching. It was all pretty simple…

I started to write a response but remembered rich hickey talk I went to where he lays out problems with java style concurrency

Clojure Concurrency - Rich Hickey https://www.youtube.com/watch?v=dGVqrGmwOAw

Even though the talk is called clojure concurrency, first half of the talk is about the problems clojure solving in traditional concurrency.

one my favorite talks I ever went to.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#62

the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…

> This will invalidate the arguments for Erlang concurrency model.

What about failure domains? As far as I'm concerned, this is the strongest reason for actor-based concurrency. I can design my architecture so that groups of processes that need to die together die together. And it's usually one or two lines of code, if any.

Here's a real life example. I have a process that maintains an SSH connection to a host machine, and that ssh connection is used to query information about running VMs on that host machine. If the SSH connection dies, it kills the process that is tracking the host machine, which in turn kills the processes tracking the associated VMs, without perturbing any of the other hosts' processes or vms. This triggers the host process to be restarted by a supervisor, which then creates a new SSH connection to query for information (possibly repopulating VM processes for tracking information). All of this I wrote zero lines of code for (which, importantly, means I made no mistakes), just one or two configuration options. More importantly, the system doesn't get stuck in an undefined state where complex query failures can cause logjams in the running system.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#63

> Programming with concurrency primitives is a difficult task because of the challenges created by its shared memory model. I never understood this often repeated point. As junior / mid-level developer I had the privilege to run self written .jar files on government scale systems with more than 50 cores. I used Java thread pools and concurrent data structures to do heavy cross thread caching. It was all pretty simple…

It's simply FUD and a strawman. Designed to trump up support for the alternative. I mean the actor model is fine on itself, but people like to set up a strawman problem to talk about the perceived benefit over it.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#64
post #6

Earlier quoted context omitted.

Co-author here. The problem with number crunching or maths is that it is very difficult to cut the whole computation into smaller units and pre-emptively schedule it. If it is possible for a specific use case, then it is moderately easy to replace that part with NIFs. For effective maths you need to convert the internal tagged number representation to machine native code that is also expensive. Solving these two thin…

Am I correct in saying that functions written in C do not get pre-empted like Erlang functions? If that is true, you could write computationally intense code in C within a BEAM app. But I think this misses the point. Pre-emption is really cool for concurrency abstractions, and the trade off is being less good at single threaded computation. Trying to turn Erlang into something like a Bitcoin miner is kind of like com…

Am I correct in saying that functions written in C do not get pre-empted like Erlang functions? If that is true, you could write computationally intense code in C within a BEAM app.

They cannot be pre-empted, but they must also return quickly, or risk causing lots of problems (see https://erlang.org/doc/man/erl_nif.html for slightly more detail on what this means). As such you can't just write some big function in C to do number crunching.

The NIF documentation mentions some ways around the problem, but all of them take some effort, or have tradeoffs of some sort. I was really excited when “dirty” NIFs were introduced, which can tell the BEAM that they'll run for a while, thus appearing to allow for long-running NIFs with no extra work other than setting a flag. However, it turns out that the BEAM just spins up N threads for scheduling dirty NIFs, and if you have too many such NIFs, too bad, some won't get scheduled till the others have completed. In retrospect it should have been obvious that there couldn't be a silver bullet for this problem, because it really isn't easy.

Erlang may well be my favorite language, but as you imply, it's just not going to be the right approach for everything: in my experience, it's absolutely fantastic in its niche but that niche is quite small. I think that's fine, though. For me, where Erlang does make sense, its concurrency approach makes it unbeatable, and I'll live with the performance tradeoffs. It turns out that basically all the NIFs I've had to write were just to gain access to functionality that Erlang doesn't expose (e.g. network namespaces on Linux, which are supported now, but weren't when I needed them).

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#65
post #50

Earlier quoted context omitted.

Java has a large set of higher level abstractions for concurrency. You don't have to use low level locks but you can. (And that's just Java, there's also Scala, clojure ...)

... what are they?

https://docs.oracle.com/javase/8/docs/api/java/util/concurre...

And given that you didn't know that, you really need to study them.

java.util.concurrent is one of the greatest gems of software ever written.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#66

> Programming with concurrency primitives is a difficult task because of the challenges created by its shared memory model. I never understood this often repeated point. As junior / mid-level developer I had the privilege to run self written .jar files on government scale systems with more than 50 cores. I used Java thread pools and concurrent data structures to do heavy cross thread caching. It was all pretty simple…

I started to write a response but remembered rich hickey talk I went to where he lays out problems with java style concurrency Clojure Concurrency - Rich Hickey https://www.youtube.com/watch?v=dGVqrGmwOAw Even though the talk is called clojure concurrency, first half of the talk is about the problems clojure solving in traditional concurrency. one my favorite talks I ever went to.

This version has the slides and video side by side: https://www.youtube.com/watch?v=nDAfZK8m5_8 might be easier to follow.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#67

the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…

Non-preemptive concurrency doesn't invalidate any argument. Erlang's GC is per user thread, even a primitive GC per user thread will have lower latency than Java's GC.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#68

the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…

Java's GC has to be best in class because of shared memory. In a shared nothing world doing GC in one "thread" doesn't stop other threads from executing, it also means that each heap can be very small so you might not even need to perform gc before the thread is done executing. It's truly amazing what Java is doing, but keep in mind that Erlang has worked this way for _decades_. And still, a classic web server that spins up one thread/process per request, can still potentially end up responding to the request with zero garbage collection in the best case, irrespective of load. This will not be true for Shenendoa or ZGC.

Does Java's Hot code reloading support data migration? One benefit of Erlangs model is that you can execute hooks when HCR is performed to make sure your data in memory is migrated to a new format.

But really, the most important thing about Erlangs actor model is error handling. If I spin up a process in Erlang and it fails, it won't corrupt the state of my other processes. In Java this can only be attained through disipline since all memory is shared. Also, I can very easily specify which processes should work together as units, such that if one fails, they all fail, and can be restarted together from a known working state. This, again, requires discipline in Java.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#69
post #68

the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…

Java's GC has to be best in class because of shared memory. In a shared nothing world doing GC in one "thread" doesn't stop other threads from executing, it also means that each heap can be very small so you might not even need to perform gc before the thread is done executing. It's truly amazing what Java is doing, but keep in mind that Erlang has worked this way for _decades_. And still, a classic web server that s…

Per thread GC is definitely a different approach than Java takes. The trade-off is that shared memory between Java threads is nearly free. Basically the same approach C++ uses, except Java has better concurrency primitives because its VM. Not sure about Erlang but data sharing between processes on JS and Python is very expensive and a frequent criticism of those languages. You can achieve zero garbage per request in Java. Typically high performance web frameworks like Undertow and Vert.X are designed this way. User code rarely does it but its definitely possible.

Not sure what you mean by data migration on code reloading. I suspect the mechanisms are different enough that it can't be compared. With Java you can load arbitrary new code, but changes to existing code are limited in ways that prevent data incompatibilities. For example you can add fields to existing object but you can't change the type of existing ones.

Data corruption from threading is rare in Java. I can't remember the last time I ran into it. Its easy to do but everyone is used to threads and the concurrency implementation is one of the best I've used. Java also supports thread groups to ensure that threads die and get restarted together. Its not automatic, you need to manage the groups, but I think it achieves the same.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#70

the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…

> This will invalidate the arguments for Erlang concurrency model. What about failure domains? As far as I'm concerned, this is the strongest reason for actor-based concurrency. I can design my architecture so that groups of processes that need to die together die together. And it's usually one or two lines of code, if any. Here's a real life example. I have a process that maintains an SSH connection to a host machin…

You can tie the fates of threads together in Java using thread groups. If you need more flexibility, or want it to be managed for you, Akka framework offers this. I believe Akka gives you a model very similar to Erlang.

In Java you would create a thread pool and configure it to restart the threads if they die. Each thread would wake up every so often to query SSH and dump their results into a queue. If the query threads die, the processes reading the queue at the other end have nothing to do so they won't execute. Its easy to make a consumer queue that executes some code on another thread whenever data arrives.

Java's exposure of the underlying OS threads and cheap transfer of data between threads lets people build libraries on top that offer memory models used by Erlang and others. Its not built in or quite as convenient, but you can use actors and fibers in Java if you want to.

Post reply on HN