Hacker News new | ask | show | jobs
by chrisseaton 3972 days ago
The thing I don't get about Erlang and BEAM is the idea that having lots of little processes means that your program will scale brilliantly to run in parallel.

Programming Erlang (authored by the creator of Erlang) says without any qualification at all that "Concurrent programs are made from small independent processes. Because of this, we can easily scale the system by increasing the number of processes and adding more CPUs."

When I read that I was expecting it to be followed by "ha ha... not really because of algorithmic sequential dependencies and Amdah's Law of course!" but it isn't!

You can have an infinite number of processes but if the dataflow graph they form doesn't have any parallelism then Erlang and BEAM aren't likely to be able to work any magic to make them so. Even if it did have parallelism it is only going to have so much and you certainly won't be able to arbitrarily scale it beyond that by increasing the number of processes.

What's more the typical advice about mutable shared state in Erlang is to encapsulate it safely in an process - which seems to be a recipe for further serialisation to me and so a crazy thing to promote!

5 comments

Everything you are saying is technically correct. The issue is that Erlang is trying to solve a different problem than you are describing. It sounds like you are hoping to perform some large but single task and are disappointed that Erlang can't defeat the Amdahl limitations inherent in your task. That's not Erlang's goal.

Erlang's goal is to take problems that are embarrassingly parallel in theory and make them embarrassingly parallel in practice. Serving a billion independent http requests in a distributed, parallel manner can technically be done in Java or C or assembly. But, it's very hard to do well and very easy to screw up in painful, confusing, life-wasting ways. Erlang makes it much easier to do well and much harder to screw up.

> Erlang makes it much easier to do well and much harder to screw up.

That's a feature of the language, not the VM (compare with Clojure, that does a similar thing on the JVM). You could still do all that on a higher-quality VM (simply because the effort put into it is orders-of-magnitude more than into BEAN; not because OpenJDK's people are smarter or anything).

That's a feature of the language, not the VM

Lies.

One way to look at the Erlang SMP VM is basically as a load balancer. Erlang automatically migrates processes between cores for maximum concurrent efficiency. It's basically coordinating N "tiny" Erlang VMs across all your cores and knows how to, ideally, optimally place your workload. You can even constrain the behavior to a per-core and per-scheduler level with VM options—not language options. See the +S and +SP and +SDcpu and +SDPcpu and +SDio and +sct options at http://www.erlang.org/doc/man/erl.html

There are tradeoffs between being the best language for a task and being the fastest language for a task. The more work you can move into the VM, the less you have to do as an application programmer, but potentially the slower your program may go since the VM has to discover or introspect your actions instead being told explicitly.

Our bottlenecks these days are programmer time and programmer thought correctness. Generating more work for programmers by making them write lower level code isn't the way forward even if the more work is slightly faster.

All that being said, everything has a price. Obviously never do numeric computing work in regular Python. Grab you some numpy or GPU frameworks. In the same vein, never do massively concurrent programming without Erlang or without a highly optimized event loop (but with an event loop you're limited back to one core, and on modern 48+ core systems, that's kinda pathetic).

> Erlang automatically migrates processes between cores for maximum concurrent efficiency. It's basically coordinating N "tiny" Erlang VMs across all your cores and knows how to, ideally, optimally place your workload. You can even constrain the behavior to a per-core and per-scheduler level with VM options—not language options. See the +S and +SP and +SDcpu and +SDPcpu and +SDio and +sct options at http://www.erlang.org/doc/man/erl.html

That's amazing, except that that's the work of Erlang's wrok-stealing scheduler, and as it happens, the JDK currently has the best work-stealing scheduler around.

> Our bottlenecks these days are programmer time and programmer thought correctness. Generating more work for programmers by making them write lower level code isn't the way forward even if the more work is slightly faster.

Why more work? I am for Erlang. Keep using Erlang. Just run it on the JVM. It's the same work with better results.

If I could get the JVM's JIT & serial GC performance combined with the BEAM's trivial-cost threads & thread-segregated GC, it would be sweet indeed.
> serial GC performance

HotSpot hardly ever uses a serial GC anymore. It's now parallel or parallel and concurrent.

> thread-segregated GC

You don't really want that if a shared-heap GC can buy you better performance because it's more mature and saves you all the copying.

> BEAM's trivial-cost threads

You can have that on the JVM.

That would be a nice combination.
but it isn't!

Chapter 26 of Programming Erlang, 2nd Edition, Programming Multicore CPUs, quite explicitly notes the problem of avoiding sequential bottlenecks, and even devotes an entire exercise to parallelizing a sequential program.

> The thing I don't get about Erlang and BEAM is the idea that having lots of little processes means that your program will scale brilliantly to run in parallel.

The point is scaling. Think in terms of request rate. If you know you can have millions of processes per machine and they run well in parallel, then you can handle requests with processes and stop worrying.

Sure, but you can have those millions of processes on the JVM, too and scale better because of better access to shared data than ETS.
Erlang generally encourages shared-nothing architectures. Of course, in some cases you want regions of shared memory or some other concurrent global resource, hence ETS. I see nothing wrong with ETS, it's well optimized for the Erlang term format in particular and gives you serializable updates.

Scalability and large actor counts aren't the definitive features of Erlang, though. It's supervision trees, the distribution protocol, the OTP framework, the primacy of tuples and lists as your main and highly flexible data types, a great pattern matching engine for binary formats and regular Erlang terms alike, module-level hotswapping, a crash-only programming model, the ability to have external programs benefit from Erlang semantics via external nodes and ports, so on and so forth.

Yes, not all of this is thanks to the VM in of itself. A lot of it is runtime and language features.

But it's already there in a cohesive whole. There is absolutely no reason to switch to the JVM when the EVM is a beast of its own.

> Erlang generally encourages shared-nothing architectures.

This is the language feature that `pron` keeps mentioning. Nothing about the VM is especially better for this than the JVM for instance.

> I see nothing wrong with ETS, it's well optimized for the Erlang term format in particular and gives you serializable updates.

There isn't anything wrong with it (as a complete neophyte to ETS and the EVM generally), the question is how much better it could be if it was on one of the several first rate JVMs that get so much more resources poured into them. Sharing data concurrently is precisely what the JVM is good at (especially at very large data set size). So in the cases where you need to use something like ETS, there is a lot of potential for improvement on a JVM vs EVM.

> But it's already there in a cohesive whole. There is absolutely no reason to switch to the JVM when the EVM is a beast of its own.

I don't want to speak for `pron` but I suspect what he is getting at is, the combination of the Erlang full story on the JVM would be a phenomenal bit of tech and it would be much easier (and more likely) for the Erlang bits to get ported to the JVM than it would be to bring the EVM up to the standard of any of the best JVMs.

It would be a phenomenal bit of tech anywhere. We had to go with Erlang for one piece of our product at Plum precisely because there is no analog story in Haskell to the Erlang full story. I would have loved to be able to build that specific piece in Haskell but it made little sense when considering what Erlang/OTP provides.

It's nice to say that the JVM has more resources and is better at XYZ while the BEAM VM is only better at ABC, therefore the Erlang full story should be on the JVM to reap the benefits of both; however, I think that would be unhealthy for Erlang. Different VMs present specialized focuses and I think the areas that BEAM is lacking in can be tackled and brought up to parity instead homogenizing the VM-field and adding to the kitchen-sink that the JVM already is.

I think the areas that BEAM is lacking in can be tackled and brought up to parity

Probably. But why not spend that effort on the language and libraries?

instead of homogenizing the VM-field

That makes as much sense as saying your language shouldn't run on the kitchen-sink Linux so as not to homogenized the OS field.

> There is absolutely no reason to switch to the JVM when the EVM is a beast of its own.

I think there is, if you want to concentrate your limited resources on the language and its phenomenal libraries while letting an enormous team working on the world’s second-largest open-source project take care of the VM for you, while at the same time giving you better performance and a wider reach. There are many more organizations that would adopt Erlang if it were on the JVM.

Completely ignoring the massive resources it would take to make the switch in the first place.

Erjang doesn't cut it. It's an incomplete research project that works on the basis of bytecode translation. Further, the disadvantages with regards to global GC are clearly listed. You say that it'll only keep improving, but that's essentially taking a leap of faith that the JVM developers will eventually get to parity with a feature you already have.

"Limited resources" is a red herring and FUD, plain and simple. Nor is "wider reach" guaranteed in the slightest. Wider reach is not intrinsically a good thing, either. Organizations for whom Erlang is out of reach simply because it doesn't use the JVM are absolutely petty and there is no loss from them not using it, IMO.

> Completely ignoring the massive resources it would take to make the switch in the first place.

If I'm suggesting it, I obviously believe that the cost/benefit is worthwhile. I don think the effort required is massive.

> You say that it'll only keep improving

I say that it has already improved enough.

> Organizations for whom Erlang is out of reach simply because it doesn't use the JVM are absolutely petty

Not petty, but rational. Those organizations already have millions of lines of code, and lots of knowledge and experience on the JVM, and the reasons for choosing Erlang aren't compelling enough given the adoption cost. But if you lower those costs...

All I'm suggesting is simply lowering the adoption costs of an upcomer, rather niche tech with a small ecosystem (which would also, I'm convinced, considerably improve the tech).

> [...] but you can have those millions of processes on the JVM, too [...]

In theory, yes. In practice, it's very difficult. JVM thread corresponds to a thread of host OS. Spawning and keeping those is very slow and expensive compared to Erlang's processes. You could have a pool of pre-spawned workers, but suddenly you can't spawn a worker for each connection and hope it will all work; you need to manage the pool. You also could try to implement green threads in JVM, as they are in Erlang, but you would need an entirely new compiler to insert yield points at appropriate places, or else you would get exactly the same problems with green threads as everywhere else.

Under JVM you just don't spawn a thread for each and every activity, because you would choke your system. The whole point of developing Erlang was to allow exactly this programming style in a manner safe against processing congestion.

> you need to manage the pool. You also could try to implement green threads in JVM, as they are in Erlang, but you would need an entirely new compiler to insert yield points at appropriate places.

I am talking about a new compiler. An Erlang compiler. The JVM is a virtual machine. Erlang is a language. We've already proven you can run Erlang on the JVM quite well, and that was before many pertinent improvements to the JVM and its ecosystem.

JVM still lacks some serious functions, namely, links and monitors. I'm not that sure you can emulate those on JVM reliably without implementing them as fundamental operations.

And then, there's still problem of interoperability. Even when you write your code in Erlang@JVM, it still needs to talk to Java code, which doesn't have yield points.

> JVM still lacks some serious functions, namely, links and monitors. I'm not that sure you can emulate those on JVM reliably without implementing them as fundamental operations.

You don't emulate them; you implement them -- just as BEAM does. Having them baked into the runtime serves no purpose. The JVM operates at a much lower level than BEAM -- just as BEAM is implemented in C, it could be implemented in Java, except that the really hard parts (JIT and GC) are already taken care of. Think of Java as C + JIT + GC.

> it still needs to talk to Java code, which doesn't have yield points.

That's not a problem. First, Erlang code talks to C code, which doesn't have yield points, either. Second, the JVM doesn't need to rely on yield points as much as BEAM does, because it is much more kernel-thread-friendly than BEAM.

How does Akka stack up in regards to providing these attributes on the JVM?
Akka actors are multiplexed on real JVM threads. So they implement a cooperative model of threading (i.e. you'd better not block for long inside the body of an actor)
Unless Akka provides a compiler, it doesn't allow much of the style Erlang was developed for.
I'm guessing you're the Quasar guy or am I off? Either way, I think you have benchmarked the lightweight thread approaches on JVM's. How much simultaneous concurrency can the JVM methods manage right now for say serving web requests? And how much does Erlang's best do on same machine?

I think that's an interesting and useful comparison point to start with to test your claim. This is also something I figured Java side would greatly improve on.

> brilliantly to run in parallel.

I just want to clarify it's for concurrent not parallel.

Erlang doesn't promise parallel. You can get parallel from concurrent but not the other way around and once again Erlang only enable concurrency and you may get parallel beacuse of concurrency.

I can keep quoting from the same book:

> Here’s the good news: your Erlang program might run n times faster on an n-core processor—without any changes to the program.

Sounds hopeful - and they've qualified it with might which is good.

> But you have to follow a simple set of rules. If you want your application to run faster on a multicore CPU, you’ll have to make sure that it has lots of processes, that the processes don’t interfere with each other, and that you have no sequential bottlenecks in your program.

Oh right, so as long as I have no dataflow dependencies it'll scale easily - but that's true for any language. The problems we have are when there are dependencies - and Erlang doesn't have a good solution for that in my opinion.

> Even if your program started as a gigantic sequential program, several simple changes to the program will parallelize it.

Several simple changes can parallelize an arbitrary sequential program? That's amazingly strong and obviously incorrect.

Also "you can get parallel from concurrent but not the other way around" that's not true! Vector instruction sets allow parallelism but not concurrency.

That's not true for any language. It depends heavily on how that language is interpreted or compiled to machine code. A number of languages support threads but have eg GIL's. We have to judge each on a case-by-case basis. Both the Erlang language and BEAM designed specifically to support this. Here's some details for you in a JVM comparison:

http://ds.cs.ut.ee/courses/course-files/To303nis%20Pool%20.p...

Unfortunately, that comparison lacks a lot of pertinent information on the JVM like new GCs, new schedulers, new, better JITs and various lightweight-thread implementations (it does mention Quasar, but doesn't understand that it works just like BEAM. BEAM also instruments your code, and in BEAM you can also accidentally block an entire kernel thread by calling a library not written in Erlang: just as you would if you were running Erlang on the JVM.

Like I said in another comment, BEAM's Erlang specificity does not mean that it's the best VM for Erlang; all data points to HotSpot (today) being a much better Erlang VM. It just means that a reasonable VM for Erlang could be developed with relatively little effort.

I think you're a little too excited about JVM's to see the point of the comment. The parent comment included a statement that code in any language can scale without dataflow dependencies. I said it actually depends on the implementation of that language as some don't realize that potential or even defeat it entirely. Gave an example with GIL followed up by examples in BEAM and JVM where implementation mattered.

Far as JVM vs BEAM, it's actually orthogonal to my comment as it would only support that implementation decisions matter for scalability on top of language's inherent traits. You've been arguing that yourself except on other side.

Per your last comment - that is what he was saying. Vector instruction sets allow parallel, which does not imply concurrency.
Ah yes you're right sorry. I was thinking without reading carefully that he was saying you can't have parallelism without concurrency.
No, Erlang does not mean that anything you write will scale. Your solution has to be broken into parallelizable pieces. But, the scalability of your solution is only as good as the mechanisms within the language and runtime to efficiently allow developers to create a scalable system. BEAM implements several nice features:

Data is immutable, so we don't have to worry about keeping data coherent between.. anything. Whether it is two processes or two nodes. New data is can be constructed with reference to old data without fear that the old data will be modified. So, "mutation" is really just new data with a reference to the old unchanged data. This greatly lowers the churn in creating new data. It also means everything can just pass (process to process or node to node) what it has without feature it will be out of date.

Everything is defined in modules. Modules define what we would think of in OOP as namespaces, structures, classes/types, and class functions. Importantly, they only define functionality. Modules do not have state. Therefore, functions accept some set of inputs, create new data from the inputs (no mutation), and return some output. This makes it very easy reason about what the code is doing if you keep the modules well defined and reasonably sized. This code can be shared around easily, too. It's got no state and is immutable.

Processes are an abstraction. You can think of them as thread, but they're really just a stack and and a little book keeping. A BEAM VM will normally of real threads equal to the number of CPUs in the machine. Each real thread will then exclusively pick a process, load the book keeping, point itself to the stack, and execute bytecode for a period of time. When done, it will mark the changes in the book keeping, and move to the next process. This is very lightweight, so literally millions can run on a single computer. Because they are self contained, they're easy to clean up. Processes also expose standard set of interfaces for communication, a pub/sub system. Again, immutable messages are sent back and forth. So, it doesn't matter if it's the same node or not.

Finally, everything is abstracted to the notion of nodes with in a cluster. By default, anything you executes on the local node, but you can specify otherwise. I can execute a module call on another machine or spawn a new process on another machine. It just means a little more information in the call, but it's the same exact concept programmatically. Also, it's possible group processes into named services. You can call a named service and it will know what processes to contact. It's a very low barrier to entry to parallelize your code if you just write it that way.

When you start thinking in terms of how structure you code for BEAM, you inherently get easy access to scalability.

> Data is immutable...

But you don't need that at the VM level. Clojure does that on the JVM. Having that at the VM level makes a simple GC work reasonably well, but HotSpot has world-class GCs that perform better, even without the assumption of immutability.

> Everything is defined in modules.

Again, that's a language-level feature.

> Processes are an abstraction

You can get that on the JVM, too.

> Finally, everything is abstracted to the notion of nodes with in a cluster.

That's the runtime library's concern. Not the VM's.

> When you start thinking in terms of how structure you code for BEAM, you inherently get easy access to scalability.

All of that is great, but implementing those features at the language/library level and harnessing HotSpot's power would give you that same easy access to even greater scalability.

Hey man, some people (like me) just don't like to work on the JVM, despite it's advantages and superior features like the GC. Just accept it.

Having worked with java, scala, jruby and closure, something always is clumsy, be it interfacing with java cruft, slow startup times of the vm, maven & co... While there are solutions to fix the solutions, its just annoying for me. I get what you say, but nevertheless are JVM (and .NET) based things nothing i'd use (except i'm forced to do so).

> Hey man, some people (like me) just don't like to work on the JVM, despite it's advantages and superior features like the GC. Just accept it.

I accept it, but the fact of the matter is that -- like it or not -- there are at least two orders of magnitude more people who use the JVM than BEAM. You're comparing the world's most popular runtime with a runtime that's not even in the top-ten.

Some of your complaints stem from exactly that difference -- the JVM is designed to operate much higher workloads than BEAM, and people use that -- hence Clojure's slow startup etc (the JVM starts up in < 80ms, BTW). But, again, your observations don't change the fact that if Erlang stays on BEAM it will forever be a niche language.

Becoming ultra popular isnt an advantage at all. Look how much crap gets produced with JS or PHP, tons of crappy libraries shallow the few good ones, people write throwaway code like there is no tomorrow and real (tm) developers have to maintain this mess. And yes, I have seen code in my day job written in Java or Scala that was nearly impossible to even understand. Much Code.

Again it's obvious that the JVM is dramatically more in use than BEAM, but I like it so far. Up to now people only stumpled upon erlang when they actually needed it, now with elixir & co a few others discover that BEAM might be exactly what they need. This shows in the ecosystem, and the community of #elixir is by far the nicest I have met so far.

Just personal experience, yours might differ (obviously). Just to reiterate: Becoming too popular results nearly everytime in garbage for everyone. Just look how much stuff gets crammed in JS nowadays.