Where is Elixir and the Actor Model heading?

Is Elixir faster than Javascript? I’d love to see benchmarks showing that :slight_smile: (I’ve always assumed that Javascript would be the fastest dynamic language around due to all the effort done in optimizing its VMs

Off topic, but Lua is still faster than V8 (javascript) last time I checked.

2 Likes

I made some simple benchmark scripts so see how many processes I can spin up on my laptop with elixir and got 262,084 before the Erlang VM shut it down. I wrote a similar script in JS and it got up to 11,000 before crashing. This is of course just a personal experience and may be inaccurate.

I also did a simple operations in one minute script for both and elixir got 3,858,545 operations while JS got 5,845,228.

Actor library of some sort: yes; supervision tree like Erlang: simply not possible. Really, not possible–shared mutable state stands in the way.

1 Like

That’s merely a default, which can be overridden by a command-line argument, so the actual limit is more like 132,000,000 :wink:

3 Likes

Why not possible? Check for example https://akka.io/ You isolate state in actor and only pass messages / events.

What is really hard achieve in other languages is true process isolation , one process breaks can take down all virtual machine. But if you got into cloud you can achieve this is some other way like using some orchestration for example Kubernetes which will restart pod if some failure.

1 Like

I’m familiar with Akka. You can imitate Erlang-style supervision without the BEAM and the enforcement of non-sharing, you cannot replicate it. With Akka the “message” you pass often contains a reference to a mutable object which is an immediate “game over” for Erlang-style supervision & recovery. Granted, that’s considered an anti-pattern to do that, but it can be done, so as with any other potential bug, it will creep into large production systems. (Even more fun, in the case that you do that, you have to implement locking around access to such an object… One of the great joys of concurrent programming in the Erlang style is NO LOCKS.) This leads to advice like"Don’t use actors for concurrency. Instead, use actors for state and use futures for concurrency"" and questions like this (ugh!)

Also with Akka, you’re just using the regular process threads for scheduling, so an actor ties up its “scheduler” (again: ugh!) until it’s done processing the receipt of a message. Also with Akka, if you have even one point in your code where you accidentally call a regular Java IO routine instead of the special event-driven ones, the you have the possibility of blocking a scheduler–and although there are event-driven libraries for network & file I/O, guess what’s a blocking design intended to use a thread-per-call? JDBC! There are ways to mitigate this, but now you’re configuring ForkJoinPool’s and ExecutionContext’s (ugh! ugh!)

All of that is what happens when you attempt to implement the actor model on top of a runtime which was not designed with explicit support for it.

Kubernetes is not close to a substitute. It gives you something similar but only at a VASTLY coarser grain. Restarting a whole pod is no substitute for restarting a single Erlang process. Consider the common pattern of breaking out state into a process that does nothing but handle it, so that a failure of logic or processing does not lose state. Now consider both the network overhead of having those in separate pods, plus the overhead of configuring and maintaining the deployment–simply not practical to use this to replicate the fine-grained recovery you get with the BEAM. (Not to mention the 4-6 orders of magnitude increase in RAM of a Kubernetes pod as compared to an Erlang process.)

14 Likes
3 Likes

Exactly. And thanks for the link to that paper!

And, through the absence of any metaprogramming facilities when I looked last time, really bad at building abstractions. Which is something where Elixir shines. I see Golang as a useful systems level language and I’m watching Golang and Rust go at each other while I’m eating popcorn and coding my business-level applications in a language that makes sense for doing that.

9 Likes

Uh… no? It uses a GC, has no detailed memory control, etc… It would not be even remotely useful to build something like a system chip on or a kernel or so (compare to Rust or C or C++).

Even at the higher level Go takes more code than what is usual in languages while also being less safe (Interface{}'s Everywhere!).

5 Likes

I don’t think that Go lang can be called system level language as it has GC, where Rust can for sure can be called system level.

1 Like

Okies, I was sloppy in my use of words there. It’s a systems language in the sense that it is (apparently) a useful language for building system tooling. Hashicorp, K8s, etcetera, seem to be good examples. It’s low level, doesn’t allow - nor need - a ton of abstractions and generates the sort of tight executables that seem to be important for that sort of stuff. I keep forgetting it actually has some higher level features because all I’ve ever done with it is fix a bug or two in open source projects I used.

Probably shows me the dangers of having opinions on languages I haven’t extensively used. I’ll go back to having opinions on C now :wink:

That would be a tooling language then, like bash or python or so.

Thanks for bringing this up! Have you seen the horrors that K8s had to do to get Go to work well? They ended up building a strongly typed system on top of it, making the code look absolutely abhorrant and near unreadable (and slowing it down).

I’ve yet to see any redeeming quality of Go, it’s not faster than C, it’s not more safe than C, and even C has Go-like threading (libmill/libdill) nowadays that also outperforms Go (due to not needing to GC). Overall I don’t see the point of having such a hard to use, poorly designed language when better things like OCaml (safety, ease-of-use, speed), Python (ease of use, libraries), or Rust (speed, safety, not as easy to use but still easier than, say, haskell, plus no better threading system out currently) exist.

Except those abstractions help make your code readable, and it is not hard to build zero-cost abstractions (like C++ or Rust or most of OCaml), so if it makes your code shorter, easier to read, more maintainable, more reuseable, then why would you ever not want them?!

In ever test I’ve done so far Go executables are significantly larger than even OCaml’s (which also includes a GC and included pruned runtime), though C/C++/Rust are significantly smaller.

What higher level features is that? It’s barely only now getting a dependency system (where C/C++ has things like Hunter for years now and Rust has had Cargo since near the beginning). It’s interface system is barely glorified C-like void pointers. It’s so bad at code that you often even have to generate code externally (woo back to the 1980’s code generators instead of safe and efficient macro systems!). Really though, I’m not sure what of any feature it has could be considered higher level, even it’s threading is old-style CSP code.

C generates such wonderfully strong opinions! ^.^

5 Likes

Yup. But at least me having an opinion on C will be based on actual experience.

(love C. I call Rust “C for people who are too easily scared” :P. And that opinion is based on rumours and having read Rust’s language description two years ago)

1 Like

C and C++ are usually dynamically linked, I’m not sure about rust and OCaml, but Go links everything statically, therefore I’m always cautious with such claims.

Of course, on a user system that runs multiple things that might use the same library it makes a difference, but the companies currently paying my bills do live in a world were that is no difference. Most of the time I get well speced VMs which do only fire up systemd and the tools my team is coding. In this world, were those base images are seldom less than a gigabyte, our customers really do not care if a binary is 3 Megs + deps or 10 Megs without, as long as they can do rpm update (or similar).

This doesn’t mean though that I like Go, but it still pays my bills…

1 Like

Rust is more like Haskell for C++'ers, it’s about as far away from C as you can get.

(Hmm, this is a good analogy I think actually, need to remember it…)

For my C/C++ work I’m usually statically linked to everything but system libraries (which you should never ever ever statically link to). It’s entirely controllable in C/C++/Rust/OCaml and others. I don’t think dynamic linking is the most common outside of system libraries (which I know Go dynamically links to as well), probably a 50% split in my experience.

When I state executable size for the C++, OCaml, and Rust tests I did prior, that was with static linkage (hence why OCaml built in it’s standard library, OCaml tends to prefer static linkage for most things).

If I wanted to use a poor language and pay the bills, I’d program in Cobol (which is not actually that bad of a language to be honest). ^.^
They get paid well!

I was not aware of that; thanks for pointing it out. It’s like I was almost getting a bit worn down on my low’ish opinion of Go, then you came to my rescue and built it back up :wink:

2 Likes

That just reminded me of that similar system, Mesos, and when I found out that, yes, it was built in C++ but using libprocess which is an attempt to do OTP-in-C++, and I wondered “why didn’t they just pick Erlang?”.

3 Likes

If you don’t need multi-node and you do need intense speed, not a bad option to go. Remember the BEAM is best with heavy IO, not with heavy CPU work. :slight_smile:

Though if you do need multi-node and you focus on IO, should go with the BEAM. ^.^

1 Like