小ネタ(没ネタ)です。

単に配列を作るのに比べて、大きな Range の map が遅かったので、ちょっと考
えてみたんですが、空配列から push するのが遅いのかな、と思ったので、size
がわかる場合には、最初に空の配列を作るとき、capa をそのサイズにすると、
ちょっとはマシになるかと思いまして。

やってみると、

bench1
                           user     system      total        real
Array.new(1000000)     0.050000   0.000000   0.050000 (  0.045718)
Array.new(10000000)    0.440000   0.000000   0.440000 (  0.450678)
Array.new(100000000)   4.520000   0.080000   4.600000 (  4.609407)
(1..1000000).map       0.060000   0.000000   0.060000 (  0.061653)
(1..10000000).map      0.590000   0.010000   0.600000 (  0.944142)
(1..100000000).map     6.020000   0.180000   6.200000 (  6.873744)

が、

(1..1000000).map       0.060000   0.010000   0.070000 (  0.058909)
(1..10000000).map      0.880000   0.020000   0.900000 (  0.898898)
(1..100000000).map     5.790000   0.050000   5.840000 (  5.850037)

こうなりました。大きいときに、若干速くなってるかな、という程度ですが...。

 小さい enum について、map をすると、逆に遅くなってしまいました。

bench2
                           user     system      total        real
Array.new(1)           2.530000   0.000000   2.530000 (  2.532221)
(1..1).map (before)    2.140000   0.000000   2.140000 (  2.143456)
(1..1).map (after)     2.930000   0.000000   2.930000 (  2.926502)

 というわけで、無しですね。Range#size を毎回まともに呼び出しているから
だろうなあ。Range#size を、わざわざ最適化するのも何だし。


Index: enum.c
===================================================================
--- enum.c	(revision 50322)
+++ enum.c	(working copy)
@@ -443,10 +443,17 @@
 enum_collect(VALUE obj)
 {
     VALUE ary;
+    VALUE size = enum_size(obj, Qnil, Qnil);

     RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

-    ary = rb_ary_new();
+    if (FIXNUM_P(size)) {
+	ary = rb_ary_new_capa(FIX2INT(size));
+    }
+    else {
+	ary = rb_ary_new();
+    }
+
     rb_block_call(obj, id_each, 0, 0, collect_i, ary);

     return ary;


# bench1
require 'benchmark'
i = 0
s = 6
e = 8
Benchmark.bm(20){|x|
  (s..e).each{|n|
    x.report("Array.new(#{10 ** n})"){
      Array.new(10 ** n){|x|
        true
      }
    }
  }

  (s..e).each{|n|
    x.report("(1..#{10**n}).map"){
      m = (1..(10**n)).map{
        true
      }
    }
  }
}

# bench2
require 'benchmark'

n = 10_000_000
Benchmark.bm(20){|x|
  x.report("Array.new(1)"){
    n.times{
      Array.new(1){|x|
        true
      }
    }
  }

  x.report("(1..1).map"){
    n.times{
      m = (1..1).map{
        true
      }
    }
  }
}

-- 
// SASADA Koichi at atdot dot net