Skip to content

Commit

Permalink
Add middleware to record YJIT code_region_size (#53)
Browse files Browse the repository at this point in the history
* Add middleware to record yjit code_region_size

* We are using the rack.after_reply hook for this to not impact response
  times - https://github.blog/2022-04-11-performance-at-github-deferring-stats-with-rack-after_reply/
* Just a simple implimentation now, so we can optimise --yjit-exec-mem-size on applications, but this
  should be simple to extend to record the other metrics exposed by runtime_stats if they are needed.

* Test behaviour with and without yjit enabled
  • Loading branch information
errm authored Feb 26, 2024
1 parent 7f8331c commit 3abace1
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/promenade.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require "promenade/version"
require "promenade/setup"
require "promenade/configuration"
require "promenade/railtie" if defined? Rails::Railtie
require "promenade/prometheus"

module Promenade
Expand All @@ -25,3 +24,5 @@ def configure
end
end
end

require "promenade/railtie" if defined? Rails::Railtie
2 changes: 2 additions & 0 deletions lib/promenade/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
require "promenade/engine"
require "promenade/client/rack/http_request_duration_collector"
require "promenade/client/rack/http_request_queue_time_collector"
require "promenade/yjit/middleware"

module Promenade
class Railtie < ::Rails::Railtie
initializer "promenade.configure_rails_initialization" do
Promenade.setup
Rails.application.config.middleware.use Promenade::YJIT::Middlware if defined? ::RubyVM::YJIT
Rails.application.config.middleware.insert_after ActionDispatch::ShowExceptions,
Promenade::Client::Rack::HTTPRequestDurationCollector
Rails.application.config.middleware.insert 0,
Expand Down
22 changes: 22 additions & 0 deletions lib/promenade/yjit/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "promenade/yjit/stats"

module Promenade
module YJIT
class Middlware
RACK_AFTER_REPLY = "rack.after_reply".freeze

def initialize(app)
@app = app
end

def call(env)
if env.key?(RACK_AFTER_REPLY)
env[RACK_AFTER_REPLY] << -> {
::Promenade::YJIT::Stats.instrument
}
end
@app.call(env)
end
end
end
end
15 changes: 15 additions & 0 deletions lib/promenade/yjit/stats.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Promenade
module YJIT
class Stats
Promenade.gauge :ruby_yjit_code_region_size do
doc "Ruby YJIT code size"
end

def self.instrument
return unless defined?(::RubyVM::YJIT) && ::RubyVM::YJIT.enabled?

Promenade.metric(:ruby_yjit_code_region_size).set({}, ::RubyVM::YJIT.runtime_stats[:code_region_size])
end
end
end
end
31 changes: 31 additions & 0 deletions spec/yjit_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require "promenade/yjit/stats"

RSpec.describe Promenade::YJIT::Stats do
describe "recording yjit stats" do
it "records code_region_size" do
# This method should not blow up in any case
expect { described_class.instrument }.not_to raise_error

if defined?(RubyVM::YJIT) && defined?(RubyVM::YJIT.enable)

# We want to test that this doesn't blow up when yjit is present but isn't enabled yet
# you need to run the testsuite with yjit disabled for this to work
expect(RubyVM::YJIT.enabled?).to be_falsey
expect { described_class.instrument }.not_to raise_error

# Then we enable yjit to test the instrumentation
RubyVM::YJIT.enable
described_class.instrument

expect(Promenade.metric(:ruby_yjit_code_region_size).get).to eq RubyVM::YJIT.runtime_stats[:code_region_size]
else
version = RUBY_VERSION.match(/(\d).(\d).\d/)
major = version[1].to_i
minor = version[2].to_i
if major >= 3 && minor >= 3
flunk "YJIT must be avalibe to test properly in ruby 3.3+"
end
end
end
end
end

0 comments on commit 3abace1

Please sign in to comment.