3 ms·
Ok, I get it now. Initially I was a bit confused because you got some concepts mixed up, but it makes sense now. Basically, Akka takes tasks and runs them with
by tomp 7y ago
Ok, I get it now. Initially I was a bit confused because you got some concepts mixed up, but it makes sense now.
Basically, Akka takes tasks and runs them with a limited number of executors each on its own JVM thread. A task runs until it's done, at which point another task is chosen to be executed. This is akin to GCD queue-based dispatch.
Technically, this has nothing to do with JVM per se - JVM threads are OS threads and thus can be preempted, but by the OS only, so when the thread continues, it's still running the same Akka task! An alternative Akka implementation would run each task in its own (OS/JVM) thread, which would solve the preemption issue, but OS threads are quite expensive (though getting less so recently) so there would be a large(r) overhead. Erlang / BEAM runtime (and Golang, Haskell, and maybe others) solves this by implementing preemptive user-space threads, which bring the best of both worlds (assuming a good implementation) - ability to run millions of tasks (each with tiny overhead) while avoiding starvation (by making tasks preemptible / re-schedule-able).
- rubyn00bie 7y agoYeah, sorry for the vernacular issues. That's probably the worst part of (parallel) programming for me these days, ha! I guess IMHO, it does have something to do with the JVM because that's how it chose to implement it! :) Not to say it doesn't have its merits. Finally, yeah, the whole OS schedule pre-empting has me curious these days as I dig more in the "system" itself. I'm sort of wondering as you pointed out what sort of things I might be able to get free free that emulate the Erlang way of doing things if I look deep enough.
- tomp 7y agoIn theory, every runtime (including JVM or even C!) implementation could be implemented with arbitrary user-space preemption, but it’s rather expensive, incurs a lot of overhead. So, languages/platforms that (to some degree) optimize for performance (definitely C, but also Golang, JVM) avoid that. In particular, tight loops suffer - to support preemption, basically in every loop you need an extra condition, to check whether enough time has passes for the task to be interrupted. This turns out to be expensive enough that basically your language is useless for any kind of mathematical computations (all vector & matrix math and a lot of statistics is full of loops within loops). Golang in particular dealt with this issue extensively, not just because of starvation, but also because of GC - in order to enable garbage collection of global objects, all threads need to be paused and their local variables & stacks snapshoted. Doing that efficiently is hard. I think they were trying to make threads arbitrarily preemptable with OS signals, but that requires extreme care with implementation (you need to know where local variables are stored at every instruction!) and is not portable (no signals on Windows). E.g. https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md https://github.com/golang/proposal/blob/master/design/24543-...