ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
@ 2024-10-31  1:51 ioquatix (Samuel Williams) via ruby-core
  2024-10-31  4:46 ` [ruby-core:119647] " ioquatix (Samuel Williams) via ruby-core
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-10-31  1:51 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been reported by ioquatix (Samuel Williams).

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
The fiber scheduler performance can be negatively affected when performing blocking operations that cannot be deferred to the event loopThe 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 IO for efficient task execution.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the Fiber Scheduler, leading to delayed execution across other Fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the Scheduler's performance.

## Proposed Solution

The proposed solution in PR #11963 introduces a blocking_region hook in the Fiber Scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) code to be lifted out of the event loop, reducing the performance impact on the Scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve Fiber-based concurrency, especially in cases where non-deferrable blocking operations would otherwise delay the Fiber Scheduler.

The new Fiber Scheduler hook blocking_region is designed to accept an opaque callable object, which encapsulates work that can be offloaded to a thread pool for execution. By isolating these blocking tasks from the main event loop, this hook allows the Scheduler to maintain better responsiveness. This addition offers an efficient way to handle tasks that cannot run asynchronously within the event loop itself, leveraging multi-threading where necessary to keep Fibers running smoothly and minimize blocking impact.



-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119647] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
@ 2024-10-31  4:46 ` ioquatix (Samuel Williams) via ruby-core
  2024-11-05  7:51 ` [ruby-core:119722] " ko1 (Koichi Sasada) via ruby-core
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-10-31  4:46 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been updated by ioquatix (Samuel Williams).

Status changed from Open to Closed

Merged in https://github.com/ruby/ruby/commit/87fb44dff6409a19d12052cf0fc07ba80a4c45ac

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110301

* Author: ioquatix (Samuel Williams)
* Status: Closed
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

The new fiber scheduler hook `blocking_region` is designed to accept an opaque callable object, which encapsulates work that can be offloaded to a thread pool for execution.

## Example

```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.
```




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119722] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
  2024-10-31  4:46 ` [ruby-core:119647] " ioquatix (Samuel Williams) via ruby-core
@ 2024-11-05  7:51 ` ko1 (Koichi Sasada) via ruby-core
  2024-11-05 13:05 ` [ruby-core:119728] " Eregon (Benoit Daloze) via ruby-core
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2024-11-05  7:51 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #20855 has been updated by ko1 (Koichi Sasada).

Status changed from Closed to Open

I against this proposal because

* I can't understand what happens on this description. I couldn't understand the control flow with a given func for rb_nogvl.
* I don't introduce `blocking_region`.

I want to revert this patch with current description.




----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110390

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119728] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
  2024-10-31  4:46 ` [ruby-core:119647] " ioquatix (Samuel Williams) via ruby-core
  2024-11-05  7:51 ` [ruby-core:119722] " ko1 (Koichi Sasada) via ruby-core
@ 2024-11-05 13:05 ` Eregon (Benoit Daloze) via ruby-core
  2024-11-05 14:20 ` [ruby-core:119732] " ioquatix (Samuel Williams) via ruby-core
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-11-05 13:05 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20855 has been updated by Eregon (Benoit Daloze).


In terms of naming, `blocking_region` sounds too internal to me.
I think the C API talks about `blocking_function` (`rb_thread_call_without_gvl`, `rb_blocking_function_t` although that type shouldn't be used) and `unblock_function` (`rb_unblock_function_t`, `UBF` in `rb_thread_call_without_gvl` docs).

It's also unclear if it's safe to call such blocking functions on different threads.
In general it seems unsafe.

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110395

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119732] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (2 preceding siblings ...)
  2024-11-05 13:05 ` [ruby-core:119728] " Eregon (Benoit Daloze) via ruby-core
@ 2024-11-05 14:20 ` ioquatix (Samuel Williams) via ruby-core
  2024-11-05 16:38 ` [ruby-core:119744] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-11-05 14:20 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been updated by ioquatix (Samuel Williams).

File clipboard-202411060314-mby8k.png added

> I don't introduce blocking_region.

I am not strongly attached to the name, I just used `blocking_region` as it is quite commonly used internally for handling of blocking operations and has existed for 13 years (https://github.com/ruby/ruby/commit/919978a8d9e25d52697c0677c1f2c0ccb50b4492#diff-d5867d8e382e49f5cdef27a4d24c1a4588954f96e00925092a586659bf1b1ba4R204).

For alternative naming, how about `blocking_operation`? I also considered `thread_call_without_gvl` but I feel it's too specific as the scheduler doesn't need to care about what the work is - just that it is blocking, and some platforms like JRuby / TruffleRuby don't necessarily have the same concept of a GVL.

> I'm not sure what is the work for blocking_region() callback.

The work is an opaque object that responds to `#call` (but in the implementation, it is a `Proc` instance) which in this specific case, wraps the execution of `rb_nogvl` with the given `func` and `unblock_func`.

> I can't understand what happens on this description. I couldn't understand the control flow with a given func for rb_nogvl.

If `rb_nogvl` is called in the fiber scheduler, it can introduce latency, as releasing the GVL will prevent the event loop from progressing while `nogvl` function is executing. To avoid this, we wrap the arguments given to `rb_nogvl` into a Proc and invoke the fiber scheduler hook, so it can decide how to execute the work.

The most basic (blocking) implementation would be something like this:

```ruby
def blocking_region(work)
  Fiber.blocking(&work)
end
```

Alternatively, using a thread:

```ruby
def blocking_region(work)
  Thread.new(&work).join
end
```

In terms of flow control, it's the same as all the fiber scheduler hooks, it routes the operation to the fiber scheduler for execution. The scheduler is allowed, within reason, to determine the execution policy.

![](clipboard-202411060314-mby8k.png)

> In general it seems unsafe.

It's a fair point - I found bugs in `zlib.c` because of this work, so for sure there exists problematic code... But I also don't want to be too reductionistic regarding "unsafe C code" being a problem... 

One option to mitigate this risk is to introduce a flag passed to `rb_nogvl` to either allow or prevent moving `func` to a different thread. I think this has wider value too - in the M:N scheduler, knowing whether blocking operations can be moved between threads might be extremely useful for similar reasons. Depending on how risk-adverse we are, we could decide to default to "allow by default" or "prevent by default". I'm personally leaning more towards allow by default as I think most usage of `rb_nogvl` should be safe in practice, but I'd also be okay with being conservative by default. The only problem I see with being conservative by default is a lot of performance may get left on the table until code is updated to use said flags.

> I want to revert this patch with current description.

Sorry @ko1, I was just excited to try this feature out. I've been running tests in `async` and other projects downstream using `ruby-head` to evaluate it. Why don't we discuss this at the developer meeting and figure out a path forward? If we still can't come to a  conclusion we can revert it. How about that?

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110398

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119744] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (3 preceding siblings ...)
  2024-11-05 14:20 ` [ruby-core:119732] " ioquatix (Samuel Williams) via ruby-core
@ 2024-11-05 16:38 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-11-05 16:56 ` [ruby-core:119745] " kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-11-05 16:38 UTC (permalink / raw)
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20855 has been updated by Dan0042 (Daniel DeLorme).


I also don't understand this patch. `Zlib.deflate` is a CPU-bound operation right? So it makes sense for Fibers of the same Thread to execute the 10 operations sequentially. That's what Fibers are about. If you want parallelism you need Threads or Ractors. It seems like this is transforming Fiber::Scheduler into a generic FiberAndOrThreadScheduler? That can't be right.

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110408

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119745] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (4 preceding siblings ...)
  2024-11-05 16:38 ` [ruby-core:119744] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-11-05 16:56 ` kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core
  2024-11-05 21:49 ` [ruby-core:119753] " ioquatix (Samuel Williams) via ruby-core
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core @ 2024-11-05 16:56 UTC (permalink / raw)
  To: ruby-core; +Cc: kjtsanaktsidis (KJ Tsanaktsidis)

Issue #20855 has been updated by kjtsanaktsidis (KJ Tsanaktsidis).


> If rb_nogvl is called in the fiber scheduler, it can introduce latency, as releasing the GVL will prevent the event loop from progressing while nogvl function is executing

I definitely understand the problem here. But… dealing with a heterogenous mix of compute and IO operations is exactly what the M:N scheduler is for, right? In that design, a Ruby thread in a rb_nogvl block pins the native thread, but other Ruby threads can run on a different native thread.

I don’t think we want a _second_ way for Ruby threads to be moved around between native threads. Should this be integrated with the M:N scheduler in some way?

IMHO I think it should be the responsibility of applications like falcon, etc to explicitly offload heavy work like zlib onto an application managed thread pool.

I wonder if another way to tackle this problem is to add some metrics or callbacks to the fiber scheduler API, so that e.g. async could warn you when the event loop is blocked for a long time and that you should consider pulling work out into a thread pool.

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110409

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119753] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (5 preceding siblings ...)
  2024-11-05 16:56 ` [ruby-core:119745] " kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core
@ 2024-11-05 21:49 ` ioquatix (Samuel Williams) via ruby-core
  2024-11-06  4:55 ` [ruby-core:119766] " ioquatix (Samuel Williams) via ruby-core
  2024-11-06 21:25 ` [ruby-core:119782] " ioquatix (Samuel Williams) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-11-05 21:49 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been updated by ioquatix (Samuel Williams).


> Zlib.deflate is a CPU-bound operation right? So it makes sense for Fibers of the same Thread to execute the 10 operations sequentially.

Yes, `Zlib.deflate` with a big enough input can become significantly CPU bound. Yes, executing that on fibers will be completely sequential and cause significant latency in the event loop. This proposal preserves the user visible sequentiality while taking advantage of thread-level parallelism internally to the scheduler. From the user's point of view, nothing is different (code still executes sequentially), but internally, the `rb_nogvl` callback in `Zlib.deflate` will not stall the event loop. As the hook is completely optional, the user-visible semantics of the scheduler must be identical with or without this hook.

> IMHO I think it should be the responsibility of applications like falcon, etc to explicitly offload heavy work like zlib onto an application managed thread pool.

In a certain way, that's exactly what this proposal does: `rb_nogvl` provides the information we need to offload heavy work and we can take advantage of it. The goal of the fiber scheduler has always been to be transparent to the user/application/library code. So it's just a matter of where you set the bar for "explicitly" - native extensions can explicitly offload heavy work using `rb_nogvl` as that's actually the only way to define code that can run safely in parallel - and as you well know there is no similar construct in pure Ruby.

> I wonder if another way to tackle this problem is to add some metrics or callbacks to the fiber scheduler API, so that e.g. async could warn you when the event loop is blocked for a long time and that you should consider pulling work out into a thread pool.

The original design of the fiber scheduler had this, it was called `enter_blocking_region` and `exit_blocking_region`: https://bugs.ruby-lang.org/issues/16786 but they were rejected as being too strongly connected to the implementation.

Knowing there are blocking operations on the event loop is extremely valuable, so I regret not having those hooks.

> Should this be integrated with the M:N scheduler in some way?

For sure there is some overlap. However, my main concern is making the fiber scheduler is good as it can be, and blocking operations within the fiber scheduler/event loop is a reasonable criticism that has come up. So, I want a solution for the fiber scheduler. All things considered, the proposal here is a reasonable solution (pending naming and safety flags). It's about as simple as I could make it and the results show that it works pretty well - within the bounds of what's possible for parallelism in Ruby.

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110417

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119766] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (6 preceding siblings ...)
  2024-11-05 21:49 ` [ruby-core:119753] " ioquatix (Samuel Williams) via ruby-core
@ 2024-11-06  4:55 ` ioquatix (Samuel Williams) via ruby-core
  2024-11-06 21:25 ` [ruby-core:119782] " ioquatix (Samuel Williams) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-11-06  4:55 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been updated by ioquatix (Samuel Williams).


After discussing this with @ko1, we are going to (1) update the name to `blocking_operation_wait` and (2) introduce a flag to `rb_nogvl` to take a conservative approach to offloading work to the fiber scheduler.

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110430

* Author: ioquatix (Samuel Williams)
* Status: Open
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:119782] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
  2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
                   ` (7 preceding siblings ...)
  2024-11-06  4:55 ` [ruby-core:119766] " ioquatix (Samuel Williams) via ruby-core
@ 2024-11-06 21:25 ` ioquatix (Samuel Williams) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: ioquatix (Samuel Williams) via ruby-core @ 2024-11-06 21:25 UTC (permalink / raw)
  To: ruby-core; +Cc: ioquatix (Samuel Williams)

Issue #20855 has been updated by ioquatix (Samuel Williams).

Status changed from Open to Closed

Updated proposal: https://bugs.ruby-lang.org/issues/20876

----------------------------------------
Feature #20855: Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop.
https://bugs.ruby-lang.org/issues/20855#change-110451

* Author: ioquatix (Samuel Williams)
* Status: Closed
----------------------------------------
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.

## Problem Description

Fibers in Ruby are designed to improve performance and responsiveness by allowing concurrent tasks to proceed without blocking one another. However, certain operations inherently block the fiber scheduler, leading to delayed execution across other fibers. When blocking operations are inevitable, such as system or CPU bound operations without event-loop support, they create bottlenecks that degrade the scheduler's overall performance.

## Proposed Solution

The proposed solution in PR https://github.com/ruby/ruby/pull/11963 introduces a `blocking_region` hook in the fiber scheduler to improve handling of blocking operations. This addition allows code that releases the GVL (Global VM Lock) to be lifted out of the event loop, reducing the performance impact on the scheduler during blocking calls. By isolating these operations from the primary event loop, this enhancement aims to improve worst case performance in the presence of blocking operations.

### `blocking_region(work)`

The new, optional, fiber scheduler hook `blocking_region` accepts an opaque callable object `work`, which encapsulates work that can be offloaded to a thread pool for execution. If the hook is not implemented `rb_nogvl` executes as usual.

## Example

```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.
```


---Files--------------------------------
clipboard-202411060314-mby8k.png (120 KB)


-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-11-06 21:25 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-10-31  1:51 [ruby-core:119646] [Ruby master Feature#20855] Introduce `Fiber::Scheduler#blocking_region` to avoid stalling the event loop ioquatix (Samuel Williams) via ruby-core
2024-10-31  4:46 ` [ruby-core:119647] " ioquatix (Samuel Williams) via ruby-core
2024-11-05  7:51 ` [ruby-core:119722] " ko1 (Koichi Sasada) via ruby-core
2024-11-05 13:05 ` [ruby-core:119728] " Eregon (Benoit Daloze) via ruby-core
2024-11-05 14:20 ` [ruby-core:119732] " ioquatix (Samuel Williams) via ruby-core
2024-11-05 16:38 ` [ruby-core:119744] " Dan0042 (Daniel DeLorme) via ruby-core
2024-11-05 16:56 ` [ruby-core:119745] " kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core
2024-11-05 21:49 ` [ruby-core:119753] " ioquatix (Samuel Williams) via ruby-core
2024-11-06  4:55 ` [ruby-core:119766] " ioquatix (Samuel Williams) via ruby-core
2024-11-06 21:25 ` [ruby-core:119782] " ioquatix (Samuel Williams) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).