[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)