mirror of
https://github.com/ruby/ruby.git
synced 2025-08-23 13:04:13 +02:00
Add a cache for class variables
This change implements a cache for class variables. Previously there was
no cache for cvars. Cvar access is slow due to needing to travel all the
way up th ancestor tree before returning the cvar value. The deeper the
ancestor tree the slower cvar access will be.
The benefits of the cache are more visible with a higher number of
included modules due to the way Ruby looks up class variables. The
benchmark here includes 26 modules and shows with the cache, this branch
is 6.5x faster when accessing class variables.
```
compare-ruby: ruby 3.1.0dev (2021-03-15T06:22:34Z master 9e5105ca45
) [x86_64-darwin19]
built-ruby: ruby 3.1.0dev (2021-03-15T12:12:44Z add-cache-for-clas.. c6be0093ae) [x86_64-darwin19]
| |compare-ruby|built-ruby|
|:--------|-----------:|---------:|
|vm_cvar | 5.681M| 36.980M|
| | -| 6.51x|
```
Benchmark.ips calling `ActiveRecord::Base.logger` from within a Rails
application. ActiveRecord::Base.logger has 71 ancestors. The more
ancestors a tree has, the more clear the speed increase. IE if Base had
only one ancestor we'd see no improvement. This benchmark is run on a
vanilla Rails application.
Benchmark code:
```ruby
require "benchmark/ips"
require_relative "config/environment"
Benchmark.ips do |x|
x.report "logger" do
ActiveRecord::Base.logger
end
end
```
Ruby 3.0 master / Rails 6.1:
```
Warming up --------------------------------------
logger 155.251k i/100ms
Calculating -------------------------------------
```
Ruby 3.0 with cvar cache / Rails 6.1:
```
Warming up --------------------------------------
logger 1.546M i/100ms
Calculating -------------------------------------
logger 14.857M (± 4.8%) i/s - 74.198M in 5.006202s
```
Lastly we ran a benchmark to demonstate the difference between master
and our cache when the number of modules increases. This benchmark
measures 1 ancestor, 30 ancestors, and 100 ancestors.
Ruby 3.0 master:
```
Warming up --------------------------------------
1 module 1.231M i/100ms
30 modules 432.020k i/100ms
100 modules 145.399k i/100ms
Calculating -------------------------------------
1 module 12.210M (± 2.1%) i/s - 61.553M in 5.043400s
30 modules 4.354M (± 2.7%) i/s - 22.033M in 5.063839s
100 modules 1.434M (± 2.9%) i/s - 7.270M in 5.072531s
Comparison:
1 module: 12209958.3 i/s
30 modules: 4354217.8 i/s - 2.80x (± 0.00) slower
100 modules: 1434447.3 i/s - 8.51x (± 0.00) slower
```
Ruby 3.0 with cvar cache:
```
Warming up --------------------------------------
1 module 1.641M i/100ms
30 modules 1.655M i/100ms
100 modules 1.620M i/100ms
Calculating -------------------------------------
1 module 16.279M (± 3.8%) i/s - 82.038M in 5.046923s
30 modules 15.891M (± 3.9%) i/s - 79.459M in 5.007958s
100 modules 16.087M (± 3.6%) i/s - 81.005M in 5.041931s
Comparison:
1 module: 16279458.0 i/s
100 modules: 16087484.6 i/s - same-ish: difference falls within error
30 modules: 15891406.2 i/s - same-ish: difference falls within error
```
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
parent
c9e02d8919
commit
e8ae922b62
Notes:
git
2021-05-12 04:04:51 +09:00
15 changed files with 219 additions and 23 deletions
5
vm.c
5
vm.c
|
@ -405,6 +405,7 @@ unsigned int ruby_vm_event_local_num;
|
|||
|
||||
rb_serial_t ruby_vm_global_constant_state = 1;
|
||||
rb_serial_t ruby_vm_class_serial = 1;
|
||||
rb_serial_t ruby_vm_global_cvar_state = 1;
|
||||
|
||||
static const struct rb_callcache vm_empty_cc = {
|
||||
.flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
|
||||
|
@ -484,7 +485,7 @@ rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
|
|||
static VALUE
|
||||
vm_stat(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
static VALUE sym_global_constant_state, sym_class_serial;
|
||||
static VALUE sym_global_constant_state, sym_class_serial, sym_global_cvar_state;
|
||||
VALUE arg = Qnil;
|
||||
VALUE hash = Qnil, key = Qnil;
|
||||
|
||||
|
@ -505,6 +506,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
|
|||
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
|
||||
S(global_constant_state);
|
||||
S(class_serial);
|
||||
S(global_cvar_state);
|
||||
#undef S
|
||||
}
|
||||
|
||||
|
@ -516,6 +518,7 @@ vm_stat(int argc, VALUE *argv, VALUE self)
|
|||
|
||||
SET(global_constant_state, ruby_vm_global_constant_state);
|
||||
SET(class_serial, ruby_vm_class_serial);
|
||||
SET(global_cvar_state, ruby_vm_global_cvar_state);
|
||||
#undef SET
|
||||
|
||||
if (!NIL_P(key)) { /* matched key should return above */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue