summaryrefslogtreecommitdiff
path: root/benchmark
Commit message (Collapse)AuthorAgeFilesLines
* Finer-grained constant cache invalidation (take 2)Kevin Newton2022-04-011-0/+22
| | | | | | | | | | | | | | | | | | | | | | | | This commit reintroduces finer-grained constant cache invalidation. After 8008fb7 got merged, it was causing issues on token-threaded builds (such as on Windows). The issue was that when you're iterating through instruction sequences and using the translator functions to get back the instruction structs, you're either using `rb_vm_insn_null_translator` or `rb_vm_insn_addr2insn2` depending if it's a direct-threading build. `rb_vm_insn_addr2insn2` does some normalization to always return to you the non-trace version of whatever instruction you're looking at. `rb_vm_insn_null_translator` does not do that normalization. This means that when you're looping through the instructions if you're trying to do an opcode comparison, it can change depending on the type of threading that you're using. This can be very confusing. So, this commit creates a new translator function `rb_vm_insn_normalizing_translator` to always return the non-trace version so that opcode comparisons don't have to worry about different configurations. [Feature #18589]
* Revert "Finer-grained inline constant cache invalidation"Nobuyoshi Nakada2022-03-251-22/+0
| | | | | | | | | | | | This reverts commits for [Feature #18589]: * 8008fb7352abc6fba433b99bf20763cf0d4adb38 "Update formatting per feedback" * 8f6eaca2e19828e92ecdb28b0fe693d606a03f96 "Delete ID from constant cache table if it becomes empty on ISEQ free" * 629908586b4bead1103267652f8b96b1083573a8 "Finer-grained inline constant cache invalidation" MSWin builds on AppVeyor have been crashing since the merger.
* Finer-grained inline constant cache invalidationKevin Newton2022-03-241-0/+22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Current behavior - caches depend on a global counter. All constant mutations cause caches to be invalidated. ```ruby class A B = 1 end def foo A::B # inline cache depends on global counter end foo # populate inline cache foo # hit inline cache C = 1 # global counter increments, all caches are invalidated foo # misses inline cache due to `C = 1` ``` Proposed behavior - caches depend on name components. Only constant mutations with corresponding names will invalidate the cache. ```ruby class A B = 1 end def foo A::B # inline cache depends constants named "A" and "B" end foo # populate inline cache foo # hit inline cache C = 1 # caches that depend on the name "C" are invalidated foo # hits inline cache because IC only depends on "A" and "B" ``` Examples of breaking the new cache: ```ruby module C # Breaks `foo` cache because "A" constant is set and the cache in foo depends # on "A" and "B" class A; end end B = 1 ``` We expect the new cache scheme to be invalidated less often because names aren't frequently reused. With the cache being invalidated less, we can rely on its stability more to keep our constant references fast and reduce the need to throw away generated code in YJIT.
* Constant time class to class ancestor lookupJohn Hawthorn2022-02-231-0/+27
| | | | | | | | | | | | | | | | | | | | | | | | | Previously when checking ancestors, we would walk all the way up the ancestry chain checking each parent for a matching class or module. I believe this was especially unfriendly to CPU cache since for each step we need to check two cache lines (the class and class ext). This check is used quite often in: * case statements * rescue statements * Calling protected methods * Class#is_a? * Module#=== * Module#<=> I believe it's most common to check a class against a parent class, to this commit aims to improve that (unfortunately does not help checking for an included Module). This is done by storing on each class the number and an array of all parent classes, in order (BasicObject is at index 0). Using this we can check whether a class is a subclass of another in constant time since we know the location to expect it in the hierarchy.
* Speed up and avoid kwarg hash alloc in Time.nowJohn Hawthorn2022-01-121-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously Time.now was switched to use Time.new as it added support for the in: argument. Unfortunately because Class#new is a cfunc this requires always allocating a Hash. This commit switches Time.now back to using a builtin time_s_now. This avoids the extra Hash allocation and is about 3x faster. $ benchmark-driver -e './ruby;3.1::~/.rubies/ruby-3.1.0/bin/ruby;3.0::~/.rubies/ruby-3.0.2/bin/ruby' benchmark/time_now.yml Warming up -------------------------------------- Time.now 6.704M i/s - 6.710M times in 1.000814s (149.16ns/i, 328clocks/i) Time.now(in: "+09:00") 2.003M i/s - 2.112M times in 1.054330s (499.31ns/i) Calculating ------------------------------------- ./ruby 3.1 3.0 Time.now 7.693M 2.763M 6.394M i/s - 20.113M times in 2.614428s 7.278710s 3.145572s Time.now(in: "+09:00") 2.030M 1.260M 1.617M i/s - 6.008M times in 2.960132s 4.769378s 3.716537s Comparison: Time.now ./ruby: 7693129.7 i/s 3.0: 6394109.2 i/s - 1.20x slower 3.1: 2763282.5 i/s - 2.78x slower Time.now(in: "+09:00") ./ruby: 2029757.4 i/s 3.0: 1616652.3 i/s - 1.26x slower 3.1: 1259776.2 i/s - 1.61x slower
* Prepare for removing RubyVM::JIT (#5262)Takashi Kokubun2021-12-132-7/+7
|
* Optimize dynamic string interpolation for symbol/true/false/nil/0-9Jeremy Evans2021-11-1810-0/+67
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This provides a significant speedup for symbol, true, false, nil, and 0-9, class/module, and a small speedup in most other cases. Speedups (using included benchmarks): :symbol :: 60% 0-9 :: 50% Class/Module :: 50% nil/true/false :: 20% integer :: 10% [] :: 10% "" :: 3% One reason this approach is faster is it reduces the number of VM instructions for each interpolated value. Initial idea, approach, and benchmarks from Eric Wong. I applied the same approach against the master branch, updating it to handle the significant internal changes since this was first proposed 4 years ago (such as CALL_INFO/CALL_CACHE -> CALL_DATA). I also expanded it to optimize true/false/nil/0-9/class/module, and added handling of missing methods, refined methods, and RUBY_DEBUG. This renames the tostring insn to anytostring, and adds an objtostring insn that implements the optimization. This requires making a few functions non-static, and adding some non-static functions. This disables 4 YJIT tests. Those tests should be reenabled after YJIT optimizes the new objtostring insn. Implements [Feature #13715] Co-authored-by: Eric Wong <e@80x24.org> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com> Co-authored-by: Yusuke Endoh <mame@ruby-lang.org> Co-authored-by: Koichi Sasada <ko1@atdot.net>
* Skip string allocation in benchmark/time_at.ymlTakashi Kokubun2021-11-142-1/+2
| | | | and also drop a weird newline from benchmark/array_sample.yml.
* add benchmark/time_at.ymlKoichi Sasada2021-11-151-0/+5
| | | | | | | | | | | | | | | | | ``` ruby_2_6 ruby_2_7 ruby_3_0 master modified Time.at(0) 12.362M 11.015M 9.499M 6.615M 9.000M i/s - 32.115M times in 2.597946s 2.915517s 3.380725s 4.854651s 3.568234s Time.at(0, 500) 7.542M 7.136M 8.252M 5.707M 5.646M i/s - 20.713M times in 2.746279s 2.902556s 2.510166s 3.629644s 3.668854s Time.at(0, in: "+09:00") 1.426M 1.346M 1.565M 1.674M 1.667M i/s - 4.240M times in 2.974049s 3.149753s 2.709416s 2.533043s 2.542853s ``` ``` ruby_2_6: ruby 2.6.7p150 (2020-12-09 revision 67888) [x86_64-linux] ruby_2_7: ruby 2.7.3p140 (2020-12-09 revision 9b884df6dd) [x86_64-linux] ruby_3_0: ruby 3.0.3p150 (2021-11-06 revision 6d540c1b98) [x86_64-linux] master: ruby 3.1.0dev (2021-11-13T20:48:57Z master fc456adc6a) [x86_64-linux] modified: ruby 3.1.0dev (2021-11-15T01:12:51Z mandatory_only_bui.. b0228446db) [x86_64-linux] ```
* add benchmark/array_sample.ymlKoichi Sasada2021-11-151-0/+5
| | | | | | | | | | | | | | | | ``` ruby_2_6 ruby_2_7 ruby_3_0 master modified ary.sample 32.113M 30.146M 11.162M 10.539M 26.620M i/s - 64.882M times in 2.020428s 2.152296s 5.812981s 6.156398s 2.437325s ary.sample(2) 9.420M 8.987M 7.500M 6.973M 7.191M i/s - 25.170M times in 2.672085s 2.800616s 3.355896s 3.609534s 3.500108s ``` ``` ruby_2_6: ruby 2.6.7p150 (2020-12-09 revision 67888) [x86_64-linux] ruby_2_7: ruby 2.7.3p140 (2020-12-09 revision 9b884df6dd) [x86_64-linux] ruby_3_0: ruby 3.0.3p150 (2021-11-06 revision 6d540c1b98) [x86_64-linux] master: ruby 3.1.0dev (2021-11-13T20:48:57Z master fc456adc6a) [x86_64-linux] modified: ruby 3.1.0dev (2021-11-15T01:12:51Z mandatory_only_bui.. b0228446db) [x86_64-linux] ```
* IO::Buffer for scheduler interface.Samuel Williams2021-11-101-0/+9
|
* add vm_ivar_of_class_setKoichi Sasada2021-10-231-0/+11
| | | | benchmark for a class's ivar setter
* allow to access ivars of classes/modulesKoichi Sasada2021-10-231-0/+12
| | | | | if an ivar of a class/module refer to a shareable object, this ivar can be read from non-main Ractors.
* Use faster any_hash logic in rb_hashJohn Hawthorn2021-09-301-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | From the documentation of rb_obj_hash: > Certain core classes such as Integer use built-in hash calculations and > do not call the #hash method when used as a hash key. So if you override, say, Integer#hash it won't be used from rb_hash_aref and similar. This avoids method lookups in many common cases. This commit uses the same optimization in rb_hash, a method used internally and in the C API to get the hash value of an object. Usually this is used to build the hash of an object based on its elements. Previously it would always do a method lookup for 'hash'. This is primarily intended to speed up hashing of Arrays and Hashes, which call rb_hash for each element. compare-ruby: ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-linux] built-ruby: ruby 3.1.0dev (2021-09-29T02:13:24Z fast_hash d670bf88b2) [x86_64-linux] # Iteration per second (i/s) | |compare-ruby|built-ruby| |:----------------|-----------:|---------:| |hash_aref_array | 1.008| 1.769| | | -| 1.76x|
* Add benchmarks to create Time instancesNobuyoshi Nakada2021-09-122-0/+12
|
* Support tracing of attr_reader and attr_writerJeremy Evans2021-08-291-0/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | In vm_call_method_each_type, check for c_call and c_return events before dispatching to vm_call_ivar and vm_call_attrset. With this approach, the call cache will still dispatch directly to those functions, so this change will only decrease performance for the first (uncached) call, and even then, the performance decrease is very minimal. This approach requires that we clear the call caches when tracing is enabled or disabled. The approach currently switches all vm_call_ivar and vm_call_attrset call caches to vm_call_general any time tracing is enabled or disabled. So it could theoretically result in a slowdown for code that constantly enables or disables tracing. This approach does not handle targeted tracepoints, but from my testing, c_call and c_return events are not supported for targeted tracepoints, so that shouldn't matter. This includes a benchmark showing the performance decrease is minimal if detectable at all. Fixes [Bug #16383] Fixes [Bug #10470] Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com>
* Prefer qualified names under ThreadNobuyoshi Nakada2021-06-292-9/+9
|
* Add a cache for class variableseileencodes2021-06-181-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Redo of 34a2acdac788602c14bf05fb616215187badd504 and 931138b00696419945dc03e10f033b1f53cd50f3 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 master 9e5105c) [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>
* Improve perfomance for Integer#size method [Feature #17135] (#3476)S.H2021-06-041-0/+2
| | | | | | | | | * Improve perfomance for Integer#size method [Feature #17135] * re-run ci * Let MJIT frame skip work for Integer#size Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com>
* Implemented some NilClass method in Ruby code is faster [Feature #17054] (#3366)S.H2021-06-021-0/+6
|
* compile.c: Emit send for === calls in when statementsAlan Wu2021-05-281-0/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The checkmatch instruction with VM_CHECKMATCH_TYPE_CASE calls === without a call cache. Emit a send instruction to make the call instead. It includes a call cache. The call cache improves throughput of using when statements to check the class of a given object. This is useful for say, JSON serialization. Use of a regular send instead of checkmatch also avoids taking the VM lock every time, which is good for multi-ractor workloads. Calculating ------------------------------------- master post vm_case_classes 11.013M 16.172M i/s - 6.000M times in 0.544795s 0.371009s vm_case_lit 2.296 2.263 i/s - 1.000 times in 0.435606s 0.441826s vm_case 74.098M 64.338M i/s - 6.000M times in 0.080974s 0.093257s Comparison: vm_case_classes post: 16172114.4 i/s master: 11013316.9 i/s - 1.47x slower vm_case_lit master: 2.3 i/s post: 2.3 i/s - 1.01x slower vm_case master: 74097858.6 i/s post: 64338333.9 i/s - 1.15x slower The vm_case benchmark is a bit slower post patch, possibily due to the larger instruction sequence. The benchmark dispatches using opt_case_dispatch so was not running checkmatch and does not make the === call post patch.
* Revert "Filling cache values on cvar write"Aaron Patterson2021-05-111-20/+0
| | | | | This reverts commit 08de37f9fa3469365e6b5c964689ae2bae0eb9f3. This reverts commit e8ae922b62adb00a80d3d4c49f7d7b0e6026eaba.
* Add a cache for class variableseileencodes2021-05-111-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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>
* Eagerly allocate instance variable tables along with objectAaron Patterson2021-05-031-0/+23
| | | | | | | | This allows us to allocate the right size for the object in advance, meaning that we don't have to pay the cost of ivar table extension later. The idea is that if an object type ever became "extended" at some point, then it is very likely it will become extended again. So we may as well allocate the ivar table up front.
* [Doc] Fix a typo s/visilibity/visibility/wonda-tea-coffee2021-04-251-1/+1
|
* Evaluate multiple assignment left hand side before right hand sideJeremy Evans2021-04-211-0/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In regular assignment, Ruby evaluates the left hand side before the right hand side. For example: ```ruby foo[0] = bar ``` Calls `foo`, then `bar`, then `[]=` on the result of `foo`. Previously, multiple assignment didn't work this way. If you did: ```ruby abc.def, foo[0] = bar, baz ``` Ruby would previously call `bar`, then `baz`, then `abc`, then `def=` on the result of `abc`, then `foo`, then `[]=` on the result of `foo`. This change makes multiple assignment similar to single assignment, changing the evaluation order of the above multiple assignment code to calling `abc`, then `foo`, then `bar`, then `baz`, then `def=` on the result of `abc`, then `[]=` on the result of `foo`. Implementing this is challenging with the stack-based virtual machine. We need to keep track of all of the left hand side attribute setter receivers and setter arguments, and then keep track of the stack level while handling the assignment processing, so we can issue the appropriate topn instructions to get the receiver. Here's an example of how the multiple assignment is executed, showing the stack and instructions: ``` self # putself abc # send abc, self # putself abc, foo # send abc, foo, 0 # putobject 0 abc, foo, 0, [bar, baz] # evaluate RHS abc, foo, 0, [bar, baz], baz, bar # expandarray abc, foo, 0, [bar, baz], baz, bar, abc # topn 5 abc, foo, 0, [bar, baz], baz, abc, bar # swap abc, foo, 0, [bar, baz], baz, def= # send abc, foo, 0, [bar, baz], baz # pop abc, foo, 0, [bar, baz], baz, foo # topn 3 abc, foo, 0, [bar, baz], baz, foo, 0 # topn 3 abc, foo, 0, [bar, baz], baz, foo, 0, baz # topn 2 abc, foo, 0, [bar, baz], baz, []= # send abc, foo, 0, [bar, baz], baz # pop abc, foo, 0, [bar, baz] # pop [bar, baz], foo, 0, [bar, baz] # setn 3 [bar, baz], foo, 0 # pop [bar, baz], foo # pop [bar, baz] # pop ``` As multiple assignment must deal with splats, post args, and any level of nesting, it gets quite a bit more complex than this in non-trivial cases. To handle this, struct masgn_state is added to keep track of the overall state of the mass assignment, which stores a linked list of struct masgn_attrasgn, one for each assigned attribute. This adds a new optimization that replaces a topn 1/pop instruction combination with a single swap instruction for multiple assignment to non-aref attributes. This new approach isn't compatible with one of the optimizations previously used, in the case where the multiple assignment return value was not needed, there was no lhs splat, and one of the left hand side used an attribute setter. This removes that optimization. Removing the optimization allowed for removing the POP_ELEMENT and adjust_stack functions. This adds a benchmark to measure how much slower multiple assignment is with the correct evaluation order. This benchmark shows: * 4-9% decrease for attribute sets * 14-23% decrease for array member sets * Basically same speed for local variable sets Importantly, it shows no significant difference between the popped (where return value of the multiple assignment is not needed) and !popped (where return value of the multiple assignment is needed) cases for attribute and array member sets. This indicates the previous optimization, which was dropped in the evaluation order fix and only affected the popped case, is not important to performance. Fixes [Bug #4443]
* st.c: skip all deleted entries [Bug #17779]tompng (tomoya ishida)2021-04-111-0/+11
| | | | | Update the start entry skipping all already deleted entries. Fixes performance issue of `Hash#first` in a certain case.
* Improve Enumerable#tally performanceNobuyoshi Nakada2021-03-161-0/+4
| | | | | | | | | Iteration per second (i/s) | |compare-ruby|built-ruby| |:------|-----------:|---------:| |tally | 52.814| 114.936| | | -| 2.18x|
* Add a benchmark for RubyVM::InstructionSequence.load_from_binaryJean Boussier2021-03-101-0/+25
|
* proc.c: make bind_call use existing callable method entry when possibleJean Boussier2021-03-101-0/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The most common use case for `bind_call` is to protect from core methods being redefined, for instance a typical use: ```ruby UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name) def real_mod_name(mod) UNBOUND_METHOD_MODULE_NAME.bind_call(mod) end ``` But it's extremely common that the method wasn't actually redefined. In such case we can avoid creating a new callable method entry, and simply delegate to the receiver. This result in a 1.5-2X speed-up for the fast path, and little to no impact on the slowpath: ``` compare-ruby: ruby 3.1.0dev (2021-02-05T06:33:00Z master b2674c1fd7) [x86_64-darwin19] built-ruby: ruby 3.1.0dev (2021-02-15T10:35:17Z bind-call-fastpath d687e06615) [x86_64-darwin19] | |compare-ruby|built-ruby| |:---------|-----------:|---------:| |fastpath | 11.325M| 16.393M| | | -| 1.45x| |slowpath | 10.488M| 10.242M| | | 1.02x| -| ```
* Improve performance some Numeric methods [Feature #17632] (#4190)S.H2021-02-191-0/+13
|
* Do not run File.write while Ractors are runningTakashi Kokubun2021-02-111-12/+15
| | | | also make sure all local variables have the __bmdv_ prefix.
* Add a benchmark-driver runner for Ractor (#4172)Takashi Kokubun2021-02-103-0/+131
| | | | | | | | | | | | | * Add a benchmark-driver runner for Ractor * Process.clock_gettime(Process:CLOCK_MONOTONIC) could be slow in Ruby 3.0 Ractor * Fetching Time could also be slow * Fix a comment * Assert overriding a private method
* Simple benchmark of Float#to_sNobuyoshi Nakada2021-02-101-0/+7
|
* Improve performance Float#positive? and Float#negative? [Feature #17614] (#4160)S.H2021-02-081-0/+8
|
* Rename RubyVM::MJIT to RubyVM::JITTakashi Kokubun2021-01-132-7/+7
| | | | | | | | because the name "MJIT" is an internal code name, it's inconsistent with --jit while they are related to each other, and I want to discourage future JIT implementation-specific (e.g. MJIT-specific) APIs by this rename. [Feature #17490]
* Improve performance some Float methods [Feature #17498] (#4018)S.H2021-01-011-0/+14
|
* Allow inlining Integer#-@ and #~Takashi Kokubun2020-12-221-0/+4
| | | | | | | | | | | | | | | | | | | | | | ``` $ benchmark-driver -v --rbenv 'before --jit;after --jit' benchmark/mjit_integer.yml --filter '(comp|uminus)' before --jit: ruby 3.0.0dev (2020-12-23T05:41:44Z master 0dd4896175) +JIT [x86_64-linux] after --jit: ruby 3.0.0dev (2020-12-23T06:25:41Z master 8887d78992) +JIT [x86_64-linux] last_commit=Allow inlining Integer#-@ and #~ Calculating ------------------------------------- before --jit after --jit mjit_comp(1) 44.006M 70.417M i/s - 40.000M times in 0.908967s 0.568042s mjit_uminus(1) 44.333M 68.422M i/s - 40.000M times in 0.902255s 0.584603s Comparison: mjit_comp(1) after --jit: 70417331.4 i/s before --jit: 44005980.4 i/s - 1.60x slower mjit_uminus(1) after --jit: 68422468.8 i/s before --jit: 44333371.0 i/s - 1.54x slower ```
* fix duplicated nameKoichi Sasada2020-12-161-1/+1
|
* Guard all accesses to RubyVM::MJIT with defined?(RubyVM::MJIT) &&Benoit Daloze2020-12-041-3/+3
| | | | * Otherwise those tests, etc cannot run on alternative Ruby implementations.
* Set allocator on class creationAlan Wu2020-11-161-0/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Allocating an instance of a class uses the allocator for the class. When the class has no allocator set, Ruby looks for it in the super class (see rb_get_alloc_func()). It's uncommon for classes created from Ruby code to ever have an allocator set, so it's common during the allocation process to search all the way to BasicObject from the class with which the allocation is being performed. This makes creating instances of classes that have long ancestry chains more expensive than creating instances of classes have that shorter ancestry chains. Setting the allocator at class creation time removes the need to perform a search for the alloctor during allocation. This is a breaking change for C-extensions that assume that classes created from Ruby code have no allocator set. Libraries that setup a class hierarchy in Ruby code and then set the allocator on some parent class, for example, can experience breakage. This seems like an unusual use case and hopefully it is rare or non-existent in practice. Rails has many classes that have upwards of 60 elements in the ancestry chain and benchmark shows a significant improvement for allocating with a class that includes 64 modules. ``` pre: ruby 3.0.0dev (2020-11-12T14:39:27Z master 6325866421) post: ruby 3.0.0dev (2020-11-12T20:15:30Z cut-allocator-lookup) Comparison: allocate_8_deep post: 10336985.6 i/s pre: 8691873.1 i/s - 1.19x slower allocate_32_deep post: 10423181.2 i/s pre: 6264879.1 i/s - 1.66x slower allocate_64_deep post: 10541851.2 i/s pre: 4936321.5 i/s - 2.14x slower allocate_128_deep post: 10451505.0 i/s pre: 3031313.5 i/s - 3.45x slower ```
* Add a benchmark for polymorphic ivar settingAaron Patterson2020-11-091-0/+17
| | | | | | | This benchmark demonstrates the performance of setting an instance variable when the type of object is constantly changing. This benchmark should give us an idea of the performance of ivar setting in a polymorphic environment
* eagerly initialize ivar table when index is small enoughAaron Patterson2020-11-091-0/+14
| | | | | | | | | | | | | When the inline cache is written, the iv table will contain an entry for the instance variable. If we get an inline cache hit, then we know the iv table must contain a value for the index written to the inline cache. If the index in the inline cache is larger than the list on the object, but *smaller* than the iv index table on the class, then we can just eagerly allocate the iv list to be the same size as the iv index table. This avoids duplicate work of checking frozen as well as looking up the index for the particular instance variable name.
* Added benchmark of vm_send by variable [ci skip]Nobuyoshi Nakada2020-10-281-0/+3
|
* Improve the performance of supereileencodes2020-09-231-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This PR improves the performance of `super` calls. While working on some Rails optimizations jhawthorn discovered that `super` calls were slower than expected. The changes here do the following: 1) Adds a check for whether the call frame is not equal to the method entry iseq. This avoids the `rb_obj_is_kind_of` check on the next line which is quite slow. If the current call frame is equal to the method entry we know we can't have an instance eval, etc. 2) Changes `FL_TEST` to `FL_TEST_RAW`. This is safe because we've already done the check for `T_ICLASS` above. 3) Adds a benchmark for `T_ICLASS` super calls. 4) Note: makes a chage for `method_entry_cref` to use `const`. On master the benchmarks showed that `super` is 1.76x slower. Our changes improved the performance so that it is now only 1.36x slower. Benchmark IPS: ``` Warming up -------------------------------------- super 244.918k i/100ms method call 383.007k i/100ms Calculating ------------------------------------- super 2.280M (± 6.7%) i/s - 11.511M in 5.071758s method call 3.834M (± 4.9%) i/s - 19.150M in 5.008444s Comparison: method call: 3833648.3 i/s super: 2279837.9 i/s - 1.68x (± 0.00) slower ``` With changes: ``` Warming up -------------------------------------- super 308.777k i/100ms method call 375.051k i/100ms Calculating ------------------------------------- super 2.951M (± 5.4%) i/s - 14.821M in 5.039592s method call 3.551M (± 4.9%) i/s - 18.002M in 5.081695s Comparison: method call: 3551372.7 i/s super: 2950557.9 i/s - 1.20x (± 0.00) slower ``` Ruby VM benchmarks also showed an improvement: Existing `vm_super` benchmark`. ``` $ make benchmark ITEM=vm_super | |compare-ruby|built-ruby| |:---------|-----------:|---------:| |vm_super | 21.555M| 37.819M| | | -| 1.75x| ``` New `vm_iclass_super` benchmark: ``` $ make benchmark ITEM=vm_iclass_super | |compare-ruby|built-ruby| |:----------------|-----------:|---------:| |vm_iclass_super | 1.669M| 3.683M| | | -| 2.21x| ``` This is the benchmark script used for the benchmark-ips benchmarks: ```ruby require "benchmark/ips" class Foo def zuper; end def top; end last_method = "top" ("A".."M").each do |module_name| eval <<-EOM module #{module_name} def zuper; super; end def #{module_name.downcase} #{last_method} end end prepend #{module_name} EOM last_method = module_name.downcase end end foo = Foo.new Benchmark.ips do |x| x.report "super" do foo.zuper end x.report "method call" do foo.m end x.compare! end ``` Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org> Co-authored-by: John Hawthorn <john@hawthorn.email>
* Optimize ObjectSpace.dump_allJean Boussier2020-09-091-0/+13
| | | | | | | | | | | | | | | | | | The two main optimization are: - buffer writes for improved performance - avoid formatting functions when possible ``` | |compare-ruby|built-ruby| |:------------------|-----------:|---------:| |dump_all_string | 1.038| 195.925| | | -| 188.77x| |dump_all_file | 33.453| 139.645| | | -| 4.17x| |dump_all_dev_null | 44.030| 278.552| | | -| 6.33x| ```
* Improved Enumerable::Lazy#zipNobuyoshi Nakada2020-07-231-0/+22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |compare-ruby|built-ruby| |:-------------------|-----------:|---------:| |first_ary | 290.514k| 296.331k| | | -| 1.02x| |first_nonary | 166.954k| 169.178k| | | -| 1.01x| |first_noarg | 299.547k| 305.358k| | | -| 1.02x| |take3_ary | 129.388k| 188.360k| | | -| 1.46x| |take3_nonary | 90.684k| 112.688k| | | -| 1.24x| |take3_noarg | 131.940k| 189.471k| | | -| 1.44x| |chain-first_ary | 195.913k| 286.194k| | | -| 1.46x| |chain-first_nonary | 127.483k| 168.716k| | | -| 1.32x| |chain-first_noarg | 201.252k| 298.562k| | | -| 1.48x| |chain-take3_ary | 101.189k| 183.188k| | | -| 1.81x| |chain-take3_nonary | 75.381k| 112.301k| | | -| 1.49x| |chain-take3_noarg | 101.483k| 192.148k| | | -| 1.89x| |block | 296.696k| 292.877k| | | 1.01x| -|
* Improved Enumerable::Lazy#flat_mapNobuyoshi Nakada2020-07-231-0/+16
| | | | | | | | | | | | | | | | | | | | |compare-ruby|built-ruby| |:-------|-----------:|---------:| |num3 | 96.333k| 160.732k| | | -| 1.67x| |num10 | 96.615k| 159.150k| | | -| 1.65x| |ary2 | 103.836k| 172.787k| | | -| 1.66x| |ary10 | 109.249k| 177.252k| | | -| 1.62x| |ary20 | 106.628k| 177.371k| | | -| 1.66x| |ary50 | 107.135k| 162.282k| | | -| 1.51x| |ary100 | 106.513k| 177.626k| | | -| 1.67x|
* Optimize Array#min (#3324)Kenta Murata2020-07-181-0/+31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The benchmark result is below: | |compare-ruby|built-ruby| |:---------------|-----------:|---------:| |ary2.min | 39.105M| 39.442M| | | -| 1.01x| |ary10.min | 23.995M| 30.762M| | | -| 1.28x| |ary100.min | 6.249M| 10.783M| | | -| 1.73x| |ary500.min | 1.408M| 2.714M| | | -| 1.93x| |ary1000.min | 828.397k| 1.465M| | | -| 1.77x| |ary2000.min | 332.256k| 570.504k| | | -| 1.72x| |ary3000.min | 338.079k| 573.868k| | | -| 1.70x| |ary5000.min | 168.217k| 286.114k| | | -| 1.70x| |ary10000.min | 85.512k| 143.551k| | | -| 1.68x| |ary20000.min | 43.264k| 71.935k| | | -| 1.66x| |ary50000.min | 17.317k| 29.107k| | | -| 1.68x| |ary100000.min | 9.072k| 14.540k| | | -| 1.60x| |ary1000000.min | 872.930| 1.436k| | | -| 1.64x| compare-ruby is 9f4b7fc82e.
* Optimize Array#max (#3325)Kenta Murata2020-07-183-0/+91
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The benchmark result is below: | |compare-ruby|built-ruby| |:---------------|-----------:|---------:| |ary2.max | 38.837M| 40.830M| | | -| 1.05x| |ary10.max | 23.035M| 32.626M| | | -| 1.42x| |ary100.max | 5.490M| 11.020M| | | -| 2.01x| |ary500.max | 1.324M| 2.679M| | | -| 2.02x| |ary1000.max | 699.167k| 1.403M| | | -| 2.01x| |ary2000.max | 284.321k| 570.446k| | | -| 2.01x| |ary3000.max | 282.613k| 571.683k| | | -| 2.02x| |ary5000.max | 145.120k| 285.546k| | | -| 1.97x| |ary10000.max | 72.102k| 142.831k| | | -| 1.98x| |ary20000.max | 36.065k| 72.077k| | | -| 2.00x| |ary50000.max | 14.343k| 29.139k| | | -| 2.03x| |ary100000.max | 7.586k| 14.472k| | | -| 1.91x| |ary1000000.max | 726.915| 1.495k| | | -| 2.06x|