Module: PumaPlus::GVL
- Defined in:
- lib/puma_plus/gvl.rb,
ext/puma_plus_gvl/puma_plus_gvl.c
Overview
GVL wait instrumentation.
Measures how long threads spend runnable but unable to run because another thread in the same process holds the GVL. That number decides the hardest question the scaling controller asks: add a thread, or fork a process?
Without it, the cheap heuristic is io_fraction = 1 - cpu/service, and it is not merely imprecise but backwards in the case that matters. A thread queued behind the GVL accrues no CPU time, so a purely CPU-bound request whose wall time is mostly queueing reads as "60% IO-bound" -- and the controller adds threads to a process where more threads make latency worse. Measured directly, the same request reports a 0.60 GVL fraction and the controller forks instead.
The native extension is optional. Without it every method here returns 0 and the controller falls back to io_fraction plus gain probing, which is what it did before this existed.
Constant Summary collapse
- NATIVE =
Qtrue
Class Method Summary collapse
-
.fraction(wait_ns, service_ns) ⇒ Object
What fraction of an interval was spent waiting for the GVL.
-
.native? ⇒ Boolean
Is the native extension available?.
- .native_start! ⇒ Object
- .native_stop! ⇒ Object
- .running? ⇒ Boolean
-
.start! ⇒ Object
Begin measuring.
- .stop! ⇒ Object
- .thread_wait_ns ⇒ Object
- .thread_waits ⇒ Object
- .wait_ns ⇒ Object
- .waits ⇒ Object
Class Method Details
.fraction(wait_ns, service_ns) ⇒ Object
What fraction of an interval was spent waiting for the GVL.
Above ~0.3 means this process is GVL-bound: its threads are queueing on each other rather than doing work, and adding more of them will deepen the queue rather than raise throughput.
69 70 71 72 73 |
# File 'lib/puma_plus/gvl.rb', line 69 def fraction(wait_ns, service_ns) return 0.0 if service_ns.nil? || service_ns <= 0 wait_ns.to_f / service_ns end |
.native? ⇒ Boolean
Is the native extension available?
46 |
# File 'lib/puma_plus/gvl.rb', line 46 def native? = const_defined?(:NATIVE) && const_get(:NATIVE) |
.native_start! ⇒ Object
83 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 83 def native_start! = false |
.native_stop! ⇒ Object
95 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 95 def native_stop! = false |
.running? ⇒ Boolean
102 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 102 def running? = false |
.start! ⇒ Object
Begin measuring. Called once per worker process.
Returns false when unavailable, which is not an error: the controller simply uses its cheaper signals.
52 53 54 55 56 |
# File 'lib/puma_plus/gvl.rb', line 52 def start! return false unless native? native_start! end |
.stop! ⇒ Object
58 59 60 61 62 |
# File 'lib/puma_plus/gvl.rb', line 58 def stop! return false unless native? native_stop! end |
.thread_wait_ns ⇒ Object
122 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 122 def thread_wait_ns = 0 |
.thread_waits ⇒ Object
126 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 126 def thread_waits = 0 |
.wait_ns ⇒ Object
107 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 107 def wait_ns = 0 |
.waits ⇒ Object
111 |
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 111 def waits = 0 |