mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

Redo of34a2acdac7
and931138b006
which were reverted. GitHub PR #4340. 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 master9e5105c
) [x86_64-darwin19] built-ruby: ruby 3.1.0dev (2021-03-15T12:12:44Z add-cache-for-clas.. c6be009) [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>
84 lines
3.4 KiB
C++
84 lines
3.4 KiB
C++
#ifndef RBIMPL_INTERN_VARIABLE_H /*-*-C++-*-vi:se ft=cpp:*/
|
|
#define RBIMPL_INTERN_VARIABLE_H
|
|
/**
|
|
* @file
|
|
* @author Ruby developers <ruby-core@ruby-lang.org>
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
* modify this file, provided that the conditions mentioned in the
|
|
* file COPYING are met. Consult the file for details.
|
|
* @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
|
|
* implementation details. Don't take them as canon. They could
|
|
* rapidly appear then vanish. The name (path) of this header file
|
|
* is also an implementation detail. Do not expect it to persist
|
|
* at the place it is now. Developers are free to move it anywhere
|
|
* anytime at will.
|
|
* @note To ruby-core: remember that this header can be possibly
|
|
* recursively included from extension libraries written in C++.
|
|
* Do not expect for instance `__VA_ARGS__` is always available.
|
|
* We assume C99 for ruby itself but we don't assume languages of
|
|
* extension libraries. They could be written in C++98.
|
|
* @brief Public APIs related to names inside of a Ruby program.
|
|
*/
|
|
#include "ruby/internal/attr/noreturn.h"
|
|
#include "ruby/internal/dllexport.h"
|
|
#include "ruby/internal/value.h"
|
|
#include "ruby/st.h"
|
|
|
|
RBIMPL_SYMBOL_EXPORT_BEGIN()
|
|
|
|
/* variable.c */
|
|
VALUE rb_mod_name(VALUE);
|
|
VALUE rb_class_path(VALUE);
|
|
VALUE rb_class_path_cached(VALUE);
|
|
void rb_set_class_path(VALUE, VALUE, const char*);
|
|
void rb_set_class_path_string(VALUE, VALUE, VALUE);
|
|
VALUE rb_path_to_class(VALUE);
|
|
VALUE rb_path2class(const char*);
|
|
VALUE rb_class_name(VALUE);
|
|
VALUE rb_autoload_load(VALUE, ID);
|
|
VALUE rb_autoload_p(VALUE, ID);
|
|
VALUE rb_f_trace_var(int, const VALUE*);
|
|
VALUE rb_f_untrace_var(int, const VALUE*);
|
|
VALUE rb_f_global_variables(void);
|
|
void rb_alias_variable(ID, ID);
|
|
void rb_copy_generic_ivar(VALUE,VALUE);
|
|
void rb_free_generic_ivar(VALUE);
|
|
VALUE rb_ivar_get(VALUE, ID);
|
|
VALUE rb_ivar_set(VALUE, ID, VALUE);
|
|
VALUE rb_ivar_defined(VALUE, ID);
|
|
void rb_ivar_foreach(VALUE, int (*)(ID, VALUE, st_data_t), st_data_t);
|
|
st_index_t rb_ivar_count(VALUE);
|
|
VALUE rb_attr_get(VALUE, ID);
|
|
VALUE rb_obj_instance_variables(VALUE);
|
|
VALUE rb_obj_remove_instance_variable(VALUE, VALUE);
|
|
void *rb_mod_const_at(VALUE, void*);
|
|
void *rb_mod_const_of(VALUE, void*);
|
|
VALUE rb_const_list(void*);
|
|
VALUE rb_mod_constants(int, const VALUE *, VALUE);
|
|
VALUE rb_mod_remove_const(VALUE, VALUE);
|
|
int rb_const_defined(VALUE, ID);
|
|
int rb_const_defined_at(VALUE, ID);
|
|
int rb_const_defined_from(VALUE, ID);
|
|
VALUE rb_const_get(VALUE, ID);
|
|
VALUE rb_const_get_at(VALUE, ID);
|
|
VALUE rb_const_get_from(VALUE, ID);
|
|
void rb_const_set(VALUE, ID, VALUE);
|
|
VALUE rb_const_remove(VALUE, ID);
|
|
#if 0 /* EXPERIMENTAL: remove if no problem */
|
|
RBIMPL_ATTR_NORETURN()
|
|
VALUE rb_mod_const_missing(VALUE,VALUE);
|
|
#endif
|
|
VALUE rb_cvar_defined(VALUE, ID);
|
|
void rb_cvar_set(VALUE, ID, VALUE);
|
|
VALUE rb_cvar_get(VALUE, ID);
|
|
VALUE rb_cvar_find(VALUE, ID, VALUE*);
|
|
void rb_cv_set(VALUE, const char*, VALUE);
|
|
VALUE rb_cv_get(VALUE, const char*);
|
|
void rb_define_class_variable(VALUE, const char*, VALUE);
|
|
VALUE rb_mod_class_variables(int, const VALUE*, VALUE);
|
|
VALUE rb_mod_remove_cvar(VALUE, VALUE);
|
|
|
|
RBIMPL_SYMBOL_EXPORT_END()
|
|
|
|
#endif /* RBIMPL_INTERN_VARIABLE_H */
|