[HN Gopher] An Introduction to Ractors in Ruby
___________________________________________________________________
An Introduction to Ractors in Ruby
Author : ksec
Score : 80 points
Date : 2022-08-25 12:14 UTC (2 days ago)
(HTM) web link (blog.appsignal.com)
(TXT) w3m dump (blog.appsignal.com)
| agentultra wrote:
| I understand that it's an introductory article and that the
| audience must be generalists however I would like to see what
| this mathematical model is.
|
| They explain it with a lot of prose but give no definitions,
| properties, or proofs.
|
| Maybe they should leave out the "mathematical" part if that isn't
| what they meant.
| WJW wrote:
| I have a half-baked branch to ractorize rubocop, surely one of
| the ruby programs which would benefit most from running CPU
| intensive code in parallel.
|
| However, it has been a beast to get it working and it is
| definitely not anywhere near "done". Sharing even simple config
| objects code between ractors is extremely difficult, because most
| of the code was never written with "sharing between ractors" as a
| design concern. It is like the red/green functions thing but with
| ractors instead of async. :/
| [deleted]
| nerdponx wrote:
| This article is interesting, but as a beginner to both Ruby and
| the actor model I'm left with some questions that limit my
| confidence in what I've learned.
|
| When exactly can you send to or receive from a ractor? Are you
| expected to wrap every send and receive in some kind of error
| handler, or can you at least check with some "is_closed" method?
| When exactly do ports open and close, and how long do they stay
| open for?
| pmontra wrote:
| Ports open when the ractor is created and close when the ractor
| ends (when its code terminates.) You can send to any ractor you
| have in scope and receive if the ractor sent a reply (or you'll
| stand still and wait.) No exception wrapping is required but
| try to receive from a terminated ractor and see what happens.
| samsquire wrote:
| I wrote a parallel communicating thread actor model in Java that
| doesn't use mutuexes. I am yet to prove it is waitfree but it
| doesn't block, it always tries to move towards forward progress.
|
| I wrote a model checker in Python and it's slow (it's not
| finished yet!) as it tests every possible interleaving of the
| threads. (I test some interleavings I was worried about
| explicitly with manual specifications) I've run it a lot but
| there's the possibility there is a rare bug. But my model checker
| needs parallelising to run in reasonable time to prove the
| algorithm correct. So I need to parallelise my parallel algorithm
| checker.
|
| It's kind of based on
| https://www.cs.technion.ac.il/~erez/Papers/wfquque-ppopp.pdf in
| terms of work stealing (or helping)
|
| Every thread can communicate with every other thread and I load
| balance the pool with a simple atomic counter and modulo.
|
| https://github.com/samsquire/multiversion-concurrency-contro...
|
| For 100 parallel threads on a hexacore I get 1.75-2.3 million
| communication events a second (depending on CPU load, when I'm
| running the model checker, I get around ~1.5 million requests a
| second) on an Intel Processor Intel(R) Core(TM) i7-10710U CPU @
| 1.10GHz, 1608 Mhz, 6 Core(s), 12 Logical Processor(s). Yes it's
| an Intel NUC. Communication costs 500 nanoseconds. I plan to
| implement bidirectional communication so threads swap messages
| with each other, for double the throughput. The plan is that
| threads/actors have multiple event streams or sources of work, so
| they always have work to do even if there is contention.
|
| I don't know if it is fast or slow, as I haven't benchmarked
| golang channels, erlang actors or akka or any other actor
| framework but I do wonder what is the raw performance of a
| multithreaded actor model.
|
| If you found this comment useful or interesting, check out my
| profile. I write of this stuff everyday.
| acjohnson55 wrote:
| What I learned working with the Akka framework in Scala is that
| actors are a really nice concurrency primitive, but I don't
| generally want to be writing business logic on them directly. I
| don't know about Ruby, but with Akka, there are all of these
| higher level constructs, like Akka Streams, which make better
| building blocks for business logic.
|
| Another thing to note is that famous actor-based platforms, like
| Erlang's OTP and Scala & Java's Akka, offer a lot for building
| network distributed systems with features for dealing with
| faults. That's a big part of their appeal. Not sure whether
| Ractors offer that.
| jbotdev wrote:
| I've found it interesting to see several different concurrency
| frameworks for Ruby over the years with mixed success (e.g.
| EventMachine), along with the multiple built-in primitive
| (fibers, threads, forks). The patterns I've seen most used in the
| real world essentially involve distributing the work elsewhere,
| rather than getting concurrency right locally, for example:
|
| * Run a bunch of background workers (Sidekiq/Resque), and queue
| up a job for each item you want processed in parallel.
|
| * Provide relatively granular HTTP APIs, and have your JS
| frontend call them in parallel with AJAX, instead of having the
| server handle concurrency.
|
| I think this is just the nature of Ruby being widely used for web
| apps where performance isn't a big concern. That said, I'd love
| to see Ractor catch on, since it's a pattern built into the
| language everyone could standardize on.
| byroot wrote:
| > distributing the work elsewhere, rather than getting
| concurrency right locally
|
| It's entirely orthogonal concerns. Job queues are for "fire and
| forget" tasks. Using Sidekiq and co offer tons of advantage
| over queuing this in a local thread pool / coroutine or
| similar.
|
| It provides efficient backoff retries, work distribution,
| durability, backpressure, and a tons of other goodies.
|
| If you were to just queue these things in-process, you may
| queue more work than your process can handle which may hurt
| latency.
|
| It also make deploy of web application awkward. If you know
| jobs are externally queue, you can stop sending traffic to that
| process and once all in-flight requests are completed you know
| it's safe to stop the process. If that process may contains
| queued work, well who knows when it's safe to restart it.
|
| In process concurrency (or parallelism) is useful for other
| things (e.g. parallel queries to a service), and that's were
| you use threads / fibers / ractors / async.
| pfarrell wrote:
| I thought the Global Interpreter Lock limited how effective
| concurrency could ever get in a single Ruby runtime (excepting
| maybe JRuby). My understanding was that you could ever only get
| green threads.
|
| My info may be dated, I haven't kept up with developments in
| Ruby 3. Is the GIL not going to be a thing any more?
| byroot wrote:
| > My understanding was that you could ever only get green
| threads.
|
| Ruby 1.8 had green threads, 1.9 onwards has native threads,
| but with a GVL. And 3.2 may have N:M threads.
|
| > Is the GIL not going to be a thing any more?
|
| On paper it's already done in 3.0. What used to be the GVL is
| now one lock per Ractor. Every object belongs to a Ractor,
| and the Ractor lock need to be acquired to access the
| objects, except for mutable objects that can be shared across
| ractors.
|
| So in practice if you are not spawning any more ractor than
| the main one the VM execute your program in, then it's
| technically still a global lock, but you now have (limited)
| ways to go around it.
| jbotdev wrote:
| You can still use native threads, as long as you're not
| expecting them to improve performance with multi-core CPU
| usage. The main purpose is typically to perform several I/O
| operations in parallel (e.g. multiple external API calls).
| That's why Ruby applications often rely on "worker pools",
| which are essentially forks, to scale performance across
| cores.
|
| Edit: as the sibling comment mentions, that's part of what
| Ractor is trying to solve.
| rubyfan wrote:
| I don't think Sidekiq/Resque are trying to get concurrency
| right. I've run a number of async processing workloads using a
| variety of Ruby out of the box and framework features - as well
| as Java, Scala concurrency features/frameworks. Any of the low
| level stuff really isn't designed for the kind of higher
| functionality you get from something like a Sidekiq or Resque.
| Honestly for implementing any workload that needs consistency,
| durability or execution guarantees you need something more
| complicated than what any language's out of the box concurrency
| model gives you. Even the frameworks like EventMachine or Akka
| help with programming abstraction and the concurrent execution
| but they don't really solve high level job/queue features. They
| are just different tools that solve different problems.
| pull_my_finger wrote:
| Actors are a first class feature of Pony Lang[1], which features
| reference capabilities as it's method of guaranteeing thread
| safety at compile time. "Refcap"s have a bit of a learning curve
| but otherwise the language is syntactically a real pleasure to
| use.
|
| While learning, I worked through Practical Object Oriented Design
| in Ruby but doing the examples in Pony. It really highlights some
| of the short-comings of Ruby, that are implemented better in
| Pony. Off the top of my head: Pony has named arguments, default
| arguments, real interfaces (no need for "duck-types"), it's
| strongly typed... There are probably more but those are a few I
| noticed specifically in a book that could otherwise stand as
| promotional material for Ruby.
|
| Definitely recommend anyone interested in OO, or the Actor Model
| to check it out.
|
| [1]: https://www.ponylang.io/discover/#what-is-pony
| Mikeb85 wrote:
| I mean, the main shortcoming of Ruby is that it was designed 25
| years ago and is being used for far more than it was designed
| for...
|
| Pony is a great language though. And yes it has an amazing
| concurrency and parallelism model and showcases the actor model
| very nicely.
___________________________________________________________________
(page generated 2022-08-27 23:01 UTC)