4 ms·
I'm a bit hesitant about this article's proposition, although I'm open to be convinced. In the end there's a GIL. For pure CPU-bound workloads, your only true
by vemv 8y ago
I'm a bit hesitant about this article's proposition, although I'm open to be convinced.
In the end there's a GIL. For pure CPU-bound workloads, your only true source of parallelism will be putting more processes into the mix, be it via forking or simply spawning more processes from scratch.
But inside a given a process, it seems to me that no matter what you do (fibers/threads), only 1 CPU can do CPU-bound work at a time.
If I was to design a high-performance Ruby solution, I'd ditch forking, threading and fibers. I'd focus instead in creating low-memory-footprint, fast-startup processes (read: no Rails!) that one can comfortably spawn in parallel, be it in a single server, or in a cluster (think k8s). Max Ruby processes count: 1 per core.
- weberc2 8y agoPresumably fibers address IO bound performance.
- repsilat 8y agoMostly Ruby web app workloads aren't CPU-bound. And when they are, you're using a language that is 100 to 1000 times slower than C. That is, for a CPU-bound task, you might be able to replace your thousand node k8s cluster with one machine running code written in a faster language (before even getting into communication and load balancing and HA and all that crap.)
- timdorr 8y agoBut abandoning everything you've written in one language for the sake of a single hot path isn't really a good idea either. It's probably a more sensible idea to use a native extension to optimize just that hot path. For instance, the Rust bindings are really good: https://github.com/tildeio/helix https://github.com/tildeio/helix
- nine_k 8y agoCrystal or Elixir allow for relatively less painful migration to high(er) performance overall. But this is beyond the scope of the article.
- repsilat 8y agoSometimes. If your profile is "lumpy" enough that can be true, but often the performance problems of slow languages manifest as annoyingly flat death-by-a-thousand-cuts profiles. (Especially after the low hanging fruit has been picked.) Grinding another 5x out of a flat profile can be hard work requiring more creativity and producing less readable code than a (fairly mechanical) translation. Luckily these tasks also tend to crop up when design has stabilised and team sizes are increasing, meaning it can often coincide with the benefits of static typing becoming greater. It's not a panacea, and it's obviously both risky and costly. Shrugs. (I've also seen the other case, FWIW. Wrote some C extension code called from a Rails app, and it was the right call at the time and we got great mileage out of it.)
- nine_k 8y agoFast execution context switching is great for concurrency. Concurrency is a way to express the control flow. It can be, and often is, achieved without any parallelism, with a strictly one-thing-at-a-time execution. Parallelism is a way to have more than one execution context execute simultaneously. In a system with a GIL, it only makes sense for contexts mostly waiting for I/O. CPU-bound tasks are obviously sequenced and limited by GIL access, and no parallelism is possible.