4 ms·
The problem is that there is a leaky abstraction of concurrency in Node and Akka. Anytime you want to do some blocking computation or IO, you put the whole appl
by phamilton 12y ago
The problem is that there is a leaky abstraction of concurrency in Node and Akka. Anytime you want to do some blocking computation or IO, you put the whole application at risk. This even applies to garbage collection. There's a whole class of things that you shouldn't do in that concurrency model. You also have to be sure that none of the libraries you use are doing these things. (zealots will say "don't do blocking IO in Akka!", which missing the point entirely)
Erlang's solution shines (in addition to reasons you cite) because of its efficient scheduler. That's something that you can't have in another language without essentially building Erlang itself.
- perishabledave 12y agoI'm not too familiar with Scala nor Akka, but know that Akka uses the actor model for concurrency. How does it differ from Erlang/Elixir?
- phamilton 12y agoIn Akka, multiple actors share a pool of threads. This has a lot of implications: 1. Garbage Collection. Garbage Collection on the JVM is not isolated to actors. One actor causing a lot of memory churn will prompt a garbage collection and prevent all other actors from executing until the Garbage Collector is finished. BEAM (the erlang VM) has per process (aka per actor) garbage collection, so actors will not interfere with each other during garbage collection. 2. Shared Memory. Since the JVM does not provide proper isolation guarantees, actors can in fact access shared memory. Safety in concurrency is accomplished via best practices rather than guaranteed by the VM. The BEAM VM does not allow shared memory. It is impossible for two processes to write to the same memory. While this matches the convention in Akka, it is enforced at the VM level. 3. Scheduler. In Akka, actors are oversubscribed to threads. That's why you can have hundreds of thousands of actors on a single system. The abstraction is great, but the implementation means that calling Thread.sleep is going to tie up a thread. If you do that often enough then all the threads will get tied up and the rest of the actors will be unable to make progress. The BEAM VM handles this scheduling at a much lower level. Calling sleep on an Erlang process is no big deal. While sleep is a contrived example, the effect on the ecosystem is pretty big. Using a blocking HTTP client on Akka is a bad idea. You have to use something like Dispatch or Spray which is essentially callback based (via monadic Futures). In Erlang, you just make the blocking call and the scheduler takes care of it. Since the synchronous code is easier to reason about than asynchronous code, the Erlang solution is simpler, easier to maintain and often more performant. I've spoken with a number of seasoned Scala/Java programmers who basically tell me "Of course you shouldn't do that, just do X or Y". With Erlang, I find there are fewer situations where you need to be an expert to avoid screwing things up.
- KirinDave 12y ago3 is true because Actors are implemented on top of a language and runtime that hasn't decided to implement actors and ONLY actors. Just like Haskell, you can in fact mix paradigms of concurrency. This is generally regarded to be a positive thing! But of course it does mean you need to understand how the semantics interact. But it's weird to call out actors oversubscribing to threads in Akka as a bad thing when that's exactly what Erlang's runtime does! And by the way, it's not difficult for even C to match the classic "quarter million execution threads" claim. Java certainly can with competitive memory overheads.
- phamilton 12y agoThe problem with Akka oversubscribing is that it doesn't actually have control over execution. It can't pause an actor in order to guarantee fair scheduling. It's similar to Node and the event loop in that regard. That's a big deal in my book. Competitive memory overhead in the Erlang world for a quarter million processes is 700MB (just sleeping). You can't do that with POSIX threads, where the default pthreads stack size on Linux is 2MB. Java adds more overhead on top of POSIX threads. As far as tuning PTHREAD_STACK_MIN on x86 is generally 16K, which is quite a bit bigger. So either you aren't in the same ballpark (factor of >5) or you have basically built a scheduler yourself (which is what Akka did).
- KirinDave 12y ago> The problem with Akka oversubscribing is that it doesn't actually have control over execution. It can't pause an actor in order to guarantee fair scheduling. It's similar to Node and the event loop in that regard. That's a big deal in my book. I mean it can if you use their libraries. You just suggest that because calling out to a different I/O library is possible that we should only evaluate it in that context. This is basically a condemnation of ALL languages that host multiple abstractions. > or you have basically built a scheduler yourself (which is what Akka did). Riiiiiiight.. so... what's the problem there? Someone did what the erlang folks did on the JVM. Why is that used as a refutation here? > As far as tuning PTHREAD_STACK_MIN on x86 is generally 16K, which is quite a bit bigger. So either you aren't in the same ballpark (factor of >5) My comment was in regards to building a scheduler yourself (erlang doesn't have that many threads of parallel execution either) or using a technique with less demanding semantics like Fork-Join. A lot of people have mixed up a lot of concepts here in the attempt to discuss why they like Erlang, but the truth is that it can't be nearly so efficient at scheduling and executing parallel computation as the average Java program appealing to java.util.concurrent, but is tuned for greater concurrency. It's also true that for some classes of computation Erlang's strategy works great, and for others it'd require substantial engine improvements to make competitive. The impression given on this thread is that Elixir is the answer. I don't like that impression being left for people new to the discipline, because it's not how most people who are good at distributed systems or massively parallel AND concurrent programming think about the problem.
- KirinDave 12y ago> Anytime you want to do some blocking computation or IO, you put the whole application at risk. It is true that you should not do this, and it's a fair point. Of course, you don't do it with Erlang either! Erlang carefully takes all file I/O out of their actor model and has it so that one (and only one) process can interact with a file handle. Operations to this have to be serialized by the actor. Since there is only one implementation of I/O and it's non-blocking, this works fine, but if your application is an actor pipeline you run exactly the same risk as your mailbox overflows. Needham's Duality is real. And naturally that ignores the fact that JVM concurrency and parallelism can handle that outcome perfectly well (and in fact Akka appeals to these primitives). It's absurd to suggest that java.util.concurrent doesn't have great tools for parallel anything, let alone parallel I/O. Golang, by the way, doesn't have this restriction. The runtime supports non-blocking, parallel-friendly I/O from the ground up. And as for Node.js, well the general abstraction here is the most parallel of all. I'm not a huge fan of it and I agree it's a bit leaky, but Node's cluster stuff is serviceable and used by many. You cannot argue the actor model is essential, threadpools and actors ultimately model the same process and as Go has shown can do so with similar sharing semantics. Finally, Erlang's scheduler is not black magic nor an arcane work of wisdom. It uses well-known, well documented principles and techniques. Quite frankly, its not the secret sauce of Erlang. Erlang's secret sauce is the HUGE some of very good code for building a distributed system with all the robust goodness that OTP provides. Erlang's great, Elixir is interesting (albeit it took one stinky old syntax and exchanged it for another stinky syntax). I wanted to reply to that prior post and talk to why Erlang has a magical recipe for distributed code, not parallel code. Quite frankly, Erlang's parallelism story only makes sense in the context of its decision to make remote and local execution indistinguishable at the call site.
- phamilton 12y agoIn the context of sharing IO, Erlang does have the same problems. I was however referring to IO in the sense of "I'm going to block until I get a response." And not have to deal with callback shenanigans (be it futures or callbacks). Golang does solve these issues fairly well except for GC issues (which I've heard have gotten better).
- 12y ago