Project

General

Profile

Feature #4917

Updated by sawa (Tsuyoshi Sawada) about 4 years ago

`Kernel#Array`, As a performance improvement in certain cases, nil should respond to to_ary. 

 Kernel#Array, when passed `nil`, nil, first tries to send `to_ary`, which to_ary to nil (which actually ends up calling `method_missing`, method_missing on nil) and then tries to send `to_a`, to_a to nil which finally succeeds. When `Kernel#Array` Kernel#Array is used frequently, often, for example in library/gem code, this can have a noticeable, if relatively small, negative impact on the overall application performance. 

 For performance improvement, `nil` should respond to `to_ary`. I propose: 

 ```ruby 
 NilClass.class_eval { alias to_ary to_a } 
 ``` 

 Using the following code, 

 ```ruby 
 

     $ irb 

     > RUBY_VERSION 
      => "1.9.2" 

     > require 'benchmark' 
 
     > def bench(times) Benchmark.bmbm{|x| x.report{times.times(&Proc.new)}} end 

 

     # Sees Let's zero the scale.... 
     > bench(10_000_000) { } 

     # The "before" benchmark.... 
     > bench(10_000_000) { Array(nil) } 

     # An optimization.... 
     > NilClass.class_eval { alias to_ary to_a } 

     # The "after" benchmark.... 
     > bench(10_000_000) { Array(nil) } 
     # Much faster! 

     # Let's see how many times method_missing is called 
 actually gets called.... 
     > NilClass.class_eval { undef to_ary } 
     > class NilClass 
   
     >     alias method_missing_without_hit method_missing 
   
     >     def method_missing(name, *args, &block) 
     >       $method_missing_hits += 1 
     >       method_missing_without_hit(name, *args, &block) 
   
     >     end 
 
     > end 
 ``` 

 I measured the benchmark and `method_missing` calls. 

 ```ruby 
 NilClass.class_eval { undef to_ary } 

 

     > $method_missing_hits = 0 
 
     > bench(100_000) { Array(nil) } 
 
     # Very slow! 
     > $method_missing_hits # 
      => 200005 
 ``` 

 ```ruby 
 

     > NilClass.class_eval { alias to_ary to_a } 

 
     > $method_missing_hits = 0 
 
     > bench(100_000) { Array(nil) } 
 
     # Instantaneous! 
     > $method_missing_hits # 
      => 0 
 ``` 

 It is observed that the former is very slow. The latter is instantaneous. 

Back