Feature #20876
Updated by ioquatix (Samuel Williams) 15 days ago
This is an evolution of the previous proposal: https://bugs.ruby-lang.org/issues/20855 ## Background The current Fiber Scheduler performance can be significantly impacted by blocking operations that cannot be deferred to the event loop, particularly in high-concurrency environments where Fibers rely on non-blocking operations for efficient task execution. ## Proposal Pull Request: https://github.com/ruby/ruby/pull/12016 We will introduce a new fiber scheduler hook called `blocking_operation_work`: ```ruby class MySchduler # ... def blocking_operation_wait(work) # Example implementation: Thread.new(&work).join end end ``` We introduce a new flag for `rb_nogvl`: `RB_NOGVL_BLOCKING_OPERATION` which indicates that `rb_nogvl(func, ...)` is a blocking operation that is safe to execute on a different thread or thread pool. When a C extension invokes `rb_nogvl(..., RB_NOGVL_BLOCKING_OPERATION)`, and a fiber scheduler is available, all the arguments will be saved into a instance of a callable object (at this time a `Proc`) called `work`. When `work` is `#call`ed, it will execute `rb_nogvl` again with all the same arguments. The fiber scheduler can decide how to execute that work, e.g. on a separate thread, to mitigate the performance impact of the blocking operation on the event loop. ![](clipboard-202411071018-ytvzs.png) ![](clipboard-202411070126-fbqpn.png) ## Example Using the branch of `async` gem: https://github.com/socketry/async/pull/352/files and enabling zlib deflate to use this feature, the following performance improvement was achieved: ```ruby require "zlib" require "async" require "benchmark" DATA = Random.new.bytes(1024*1024*100) duration = Benchmark.measure do Async do 10.times do Async do Zlib.deflate(DATA) end end end end # Ruby 3.3.4: ~16 seconds # Ruby 3.4.0 + PR: ~2 seconds. ```