Show HN: Io_uring for Ruby

114 pointsposted 4 days ago
by ciconia

31 Comments

Lammy

4 days ago

Very cool. Would love to see it be Ractor-safe too, which C extensions are not by default.

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/

ciconia

4 days ago

Thanks, that should be possible. I'll put it on the project's TODO list :-).

yxhuvud

4 days ago

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.

blacklite

4 days ago

Is Ruby commonly used in high-performance applications?

Tuna-Fish

4 days ago

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.

yxhuvud

4 days ago

Dunno, but being able to do nonblocking file io could still be useful, I suppose.

Asmod4n

4 days ago

its definitely better than just having select in the base installation.

chucke

3 days ago

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

jhyaaasdfw

4 days ago

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.

davexunit

4 days ago

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.

xerxes901

4 days ago

This is in fact exactly what Ruby’s Fibers and the async fiber-scheduler gem do

slashdev

4 days ago

The whole point of IO uring is to be fast by reducing the number of system calls per request.

If you then process your requests in the slowest programming language, you add back at least 100x the overhead.

It doesn’t make sense to me.

ciconia

4 days ago

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.

[0] https://github.com/digital-fabric/iou/blob/main/examples/htt... [1] https://www.youtube.com/watch?v=qf5V02QNMnA

slashdev

4 days ago

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.

psd1

3 days ago

We get it, you vape.

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?

jcmfernandes

4 days ago

I expected this comment, just not so early in the life of this thread. Moving on... cool stuff!

adamrezich

4 days ago

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.

FridgeSeal

4 days ago

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.

adamrezich

4 days ago

You are not describing a use-case that the flagging feature on this website is intended to be used for—flags are not super-downvotes.

FridgeSeal

4 days ago

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.

dgfitz

4 days ago

This whole forum is becoming quite lame. I just haven’t found a better one yet.

carbotaniuman

4 days ago

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.

faizshah

4 days ago

I found the easiest way to explain it is Ruby/Python/Lua are scripting languages for optimized C code.

slashdev

4 days ago

A syscall is so fast compared to Ruby code. It really doesn’t make much sense to me.

yxhuvud

4 days ago

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.

slashdev

4 days ago

Well that’s true. To do async file IO without that is really just using a thread pool.

Of course, I don’t see why that won’t perform fine for Ruby.

It’s just so slow that optimizing the async IO at the OS level doesn’t matter

sliken

4 days ago

The slower the language the bigger the upside.

slashdev

4 days ago

That’s false.

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.

ckolkey

4 days ago

Oh, that meme is old, sorry to say. Here's a nice blog post about ruby outperforming C with the new JIT compiler. Fun times :)

https://railsatscale.com/2023-08-29-ruby-outperforms-c/

neonsunset

4 days ago

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://benchmarksgame-team.pages.debian.net/benchmarksgame/...

slashdev

4 days ago

In very specific, very limited cases.

I remember when these claims were made for Java twenty years ago. It’s still slower than C, most of the time.

Some things don’t change.