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

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?

Returns:

  • (Boolean)


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

Returns:

  • (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_nsObject



122
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 122

def thread_wait_ns = 0

.thread_waitsObject



126
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 126

def thread_waits = 0

.wait_nsObject



107
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 107

def wait_ns = 0

.waitsObject



111
# File 'ext/puma_plus_gvl/puma_plus_gvl.c', line 111

def waits = 0