“Ractor safety around C extensions has the following properties:
— By default, all C extensions are recognized as Ractor-unsafe.
— Ractor-unsafe C-methods may only be called from the main Ractor. If invoked by a non-main Ractor, then a Ractor::UnsafeError is raised.
— If an extension desires to be marked as Ractor-safe the extension should call rb_ext_ractor_safe(true) at the Init_ function for the extension, and all defined methods will be marked as Ractor-safe.”
I don't know enough about io_uring to know if there would be any thread-safety issues to just declaring it, but here's an example pull request from a few years ago enabling Ractor support on a C extension that had no sharing issues: https://github.com/dearblue/ruby-extattr/pull/1/
It is interesting to compare the effort required to interact with C libs in Ruby and in Crystal. I wrote https://github.com/yxhuvud/ior a couple of years ago and it is roughly on the same level of abstraction as this lib if I read it correctly. So it also allows a nice comparison between how the API is exposed.
Ior doesn't contain the multishot stuff because it didn't exist when I still added stuff to it, but apart from that it do include a whole lot of ops.
Ruby is used plenty in the kind of situations where the performance of the program itself is less important that the overhead of IO. This should help quite a lot on such loads.
That's pretty cool, the examples are quite simple to digest.
Of course, the main argument against it is that you require access to the underlying fds, which most libraries in the ecosystem abstract you away from, so there's no way to use p.ex. net-http with this (without some serious patching and low-lever ivars access). That makes its application limited.
Nevertheless, it's very interesting, thx for sharing. I'm of the opinion that it's about time ruby ships a default performant fiber scheduler
Would Ruby benefit if we put epoll(7) or io_uring(7) on IO boundaries and then do something like Go or BEAM where we preempt at a certain number of function calls or time? One challenge I see with this approach is calling into C libraries although I don't know how common that is in Ruby ecosystem.
It's pretty common to call into C libraries in Ruby. Many widely used Ruby gems have what are called "native extensions". The approach that I know of to deal with this problem is that captured continuations with C stack frames are marked as non-resumable. Trying to resume such a continuation throws an exception. As the programmer, you'd write your async code in a way that avoids calls to C that call back into Ruby to prevent capturing non-resumable continuations.
Just this morning I was benchmarking the example HTTP server I wrote using IOU [0]. The usual caveats notwithstanding, I would say 100K sustained requests per second serving 1024 concurrent connections (on a more-or-less average laptop) is anything but slow...
In fact, the whole idea for this library is to provide a low-level, fast, flexible, asynchronous I/O layer for building Ruby apps and letting Ruby+YJIT optimize the app code, which it actually is getting pretty good at.
If you're open to learning more about where the Ruby runtime is performance-wise, there was a very interesting recent talk [1] about this very subject.
As soon as you do more than hello world inside those requests, I think you'll find the difference is hardly noticeable. Try it with real requests that use the database.
The "performance" of the language is going to have approximately zero effect once a request has to schlep over to the database in a 7ms round-trip.
There's a wide field of applications where language performance has approximately zero effect on application performance. I have 15ms of latency, what do I care if your web app is written in Rust?
It shouldn't have been flagged though—what's up with that? Why would someone, let alone multiple people, flag OP's comment? It's inoffensive, doesn't violate guidelines whatsoever, and in fact asks a question that I'm sure many people (myself included) feel is quite worthy of asking, so we can read peoples' responses to it.
Using flags to shut down entirely reasonable discussion is very lame.
Probably because it’s a fairly tired argument that gets rolled out almost automatically in response to any async-IO or high speed IO work. It’s like there’s a group that likes to argue that only the cream of the crop spills get to use nice new things, and the rest of us spills be pleased with what we have already. At this point, it’s a bit “ok thank you”, it don’t add much.
I'm aware, but give people a proxy for something like downvotes, and they'll use them as such. Hackernews tends to have more restraint than reddit on that front, but not immune.
One way you can think of this is speeding up the "slowest programming language". And removing/reducing blocking calls has benefits for languages like Ruby too.
Io uring have several use cases. You describe one, but it also enables things like no blocking file IO, which nothing else provides in a useful manner. Ruby can be slow in certain scenarios, but not slow enough that things like that doesxnt matter.
If the difference between IO uring and epoll is 2x, but that was only 5% of the total runtime, the rest being slow Ruby code, then at best you get a 2.5% speed up. Not worth it.
Ruby YJIT is slower by multiple orders of magnitude if we look at more than a single constructed example.
In addition, the blog post measures the cost of avoiding doing the interop, which allows wins as the compiler improves in the case of short-lived calls implemented in a native component.
This, however, is not exclusive to Ruby and statically typed JIT or AOT compiled languages benefit from this to a much higher extent.
https://docs.ruby-lang.org/en/master/extension_rdoc.html#lab... sez:
“Ractor safety around C extensions has the following properties:
— By default, all C extensions are recognized as Ractor-unsafe.
— Ractor-unsafe C-methods may only be called from the main Ractor. If invoked by a non-main Ractor, then a Ractor::UnsafeError is raised.
— If an extension desires to be marked as Ractor-safe the extension should call rb_ext_ractor_safe(true) at the Init_ function for the extension, and all defined methods will be marked as Ractor-safe.”
I don't know enough about io_uring to know if there would be any thread-safety issues to just declaring it, but here's an example pull request from a few years ago enabling Ractor support on a C extension that had no sharing issues: https://github.com/dearblue/ruby-extattr/pull/1/