FFI presents an opaque, unoptimizable boundary of code. Having chatty code like this is going to cost a lot. To the point where this is even a factor in much faster languages with zero-cost-ish interop like C# - you still have to make a call, sometimes paying the cost of modifying state flags for VM (GC transition).
If Ruby YJIT is starting to become a measurable factor (after all, it was slower than other, purely interpreted, languages until recently), then the same rule as above will become more relevant.
If Ruby YJIT is starting to become a measurable factor (after all, it was slower than other, purely interpreted, languages until recently), then the same rule as above will become more relevant.