Hello! This page collects the code examples and perfetto visualization links for the How the Ruby Global VM Lock impacts app performance RubyConf 2024 talk.

Observing the GVL #1


Click Open Example 1 to explore this example. Alternatively, download rc-example1.json.gz and open it with the Perfetto UI. Example code is from rc-example1.rb:

require "gvl-tracing"

def counter_loop
  counter = 0
  counter += 1 while counter < 1_000_000_000
end

GvlTracing.start("rc-example1.json") do
  t2 = Thread.new { counter_loop }
  counter_loop
  t2.join
end

Observing the GVL #2


Click Open Example 2 to explore this example. Alternatively, download rc-example2.json.gz and open it with the Perfetto UI. Example code is from rc-example2.rb:

require "gvl-tracing"

def counter_loop
  counter = 0
  counter += 1 while counter < 100_000_000
end

GvlTracing.start("rc-example2.json", os_threads_view_enabled: true) do
  threads = 9.times.map { Thread.new { counter_loop } }
  counter_loop
  threads.map(&:join)
end

Observing the GVL #3


Click Open Example 3 to explore this example. Alternatively, download rc-example3.json.gz and open it with the Perfetto UI. Example code is from rc-example3.rb:

require "gvl-tracing"

def counter_loop
  counter = 0
  counter += 1 while counter < 100_000_000
end

GvlTracing.start("rc-example3.json") do
  Thread.new {
    counter_loop
    sleep
  }
  3.times { counter_loop }
end

Observing the GVL #4


Click Open Example 4 to explore this example. Alternatively, download rc-example4.json.gz and open it with the Perfetto UI. Example code is from rc-example4.rb:

require "gvl-tracing"
require "net/http"

def perform_request =
  Net::HTTP.start("www.google.com", open_timeout: 0.5, read_timeout: 0.5, write_timeout: 0.5) { |it| it.get("/") }

GvlTracing.start("rc-example4.json") do
  20.times { perform_request }
end

Observing the GVL #5


Click Open Example 5 to explore this example. Alternatively, download rc-example5.json.gz and open it with the Perfetto UI. Example code is from rc-example5.rb:

require "gvl-tracing"
require "net/http"
require "benchmark/ips"

def perform_request =
  Net::HTTP.start("www.google.com", open_timeout: 0.5, read_timeout: 0.5, write_timeout: 0.5) { |it| it.get("/") }

GvlTracing.start("rc-example5.json") do
  Benchmark.ips do |x|
    x.config(time: 1, warmup: 0)
    x.report("request") { perform_request }
  end
end

Observing the GVL #6


Click Open Example 6 to explore this example. Alternatively, download rc-example6.json.gz and open it with the Perfetto UI. Example code is from rc-example6.rb:

require "gvl-tracing"
require "net/http"
require "benchmark/ips"

def perform_request =
  Net::HTTP.start("www.google.com", open_timeout: 0.5, read_timeout: 0.5, write_timeout: 0.5) { |it| it.get("/") }

def counter_loop
  counter = 0
  counter += 1 while counter < 1_000_000_000
end

GvlTracing.start("rc-example6.json") do
  Thread.new { counter_loop }

  Benchmark.ips do |x|
    x.config(time: 1, warmup: 0)
    x.report("request") { perform_request }
  end
end

Observing the GVL #7 (Ruby 3.2)


Click Open Example 7 to explore this example. Alternatively, download rc-example7.json.gz and open it with the Perfetto UI. Example code is from rc-example7.rb:

require "gvl-tracing"
require "net/http"
require "benchmark/ips"

def perform_request =
  Net::HTTP.start("www.google.com", open_timeout: 0.5, read_timeout: 0.5, write_timeout: 0.5) { |it| it.get("/") }

def counter_loop
  counter = 0
  counter += 1 while counter < 1_000_000_000
end

GvlTracing.start("rc-example7.json") do
  Ractor.new { counter_loop }

  Benchmark.ips do |x|
    x.config(time: 1, warmup: 0)
    x.report("request") { perform_request }
  end
end

Click Open Example 7-2 to explore this example. Alternatively, download rc-example7-2.json.gz and open it with the Perfetto UI. Example code is from rc-example7-2.rb:

require "gvl-tracing"

def counter_loop
  counter = 0
  counter += 1 while counter < 100_000_000
end

GvlTracing.start("rc-example7-2.json", os_threads_view_enabled: true) do
  ractors = 9.times.map { Ractor.new { counter_loop } }
  counter_loop
  ractors.map(&:take)
end

Observing the GVL #7 (Ruby 3.3+)

Example code for both of the following is from rc-example7-2.rb, same as above; the difference is that we’re running the examples on either Ruby 3.3 or Ruby 3.4 (the latter using RUBY_MAX_CPU=4).


Click Open Example 7-2-r33 to explore this example. Alternatively, download rc-example7-2-r33.json.gz and open it with the Perfetto UI.


Click Open Example 7-2-max-cpu-4 to explore this example. Alternatively, download rc-example7-2-max-cpu-4.json.gz and open it with the Perfetto UI.

Observing the GVL #8


Click Open Example 8 to explore this example. Alternatively, download rc-example2-mn-threads.json.gz and open it with the Perfetto UI. Example code is from rc-example2.rb (Same as above, the only difference is running it with RUBY_MN_THREADS=1):

require "gvl-tracing"

def counter_loop
  counter = 0
  counter += 1 while counter < 100_000_000
end

GvlTracing.start("rc-example2.json", os_threads_view_enabled: true) do
  threads = 9.times.map { Thread.new { counter_loop } }
  counter_loop
  threads.map(&:join)
end

Further exploration

If this topic interests you, I suggest looking at my two other blog posts on the subject: