[#39863] forループの速度 — Masahiro Sato <msato@...>

15 messages 2004/07/20

[#39868] イテレータとfor文 — OOTANI TAKASHI <otn@...5.so-net.ne.jp>

大谷と申します。

31 messages 2004/07/20
[#39886] Re: イテレータとfor文 — Tietew <tietew-ml-ruby-list@...> 2004/07/21

[ruby-list:39887] Re: forループの速度

From: UENO Katsuhiro <unnie@...>
Date: 2004-07-21 04:23:25 UTC
List: ruby-list #39887
うえのです。

この辺に繋げさせて下さい。

On Wed, 21 Jul 2004 11:44:40 +0900
SASADA Koichi <ko1@atdot.net> wrote:

>   1) i<x
>   2) i+1
>
>  という"Rubyメソッドの呼び出し"が余計に必要になるため,遅くな
> ると考えられます.
>
>  Fixnum#times や Range#each は,これらを C言語のレベルで実装
> し,実行しているので,速いです.

# 某scannerを作った経験から ;p

Rubyスクリプトを速くするコツは、

  1. C言語レベルでの実行命令数を減らすこと
  2. メモリアロケーションを行わせないこと

です。

現在のRubyの実装では、ループ処理のオーバーヘッドを純粋に比較した
場合、whileループが最も高速です。しかし、ささださんもご指摘の
通り、多くの場合は条件判定などで現れる多数のメソッドの呼び出しが
ネックになりwhileループは遅くなってしまいます。

eachメソッドとイテレータ vs for では、ブロックのスコープを生成しない
分だけ for が僅かに高速です。

で、おそらく現在の実装 (ruby-1.8.1) では、内側と外側のループで
共にループカウンタが必要不可欠なのであれば、以下のコードが最も速い
のではないかと思います。(ちゃんと検証してません)

  for i in 0...1000
    for j in 0...40000
      a[i] = 0
    end
  end

また、Fixnum どうしの Range オブジェクトの each メソッドより
Fixnum#times のほうが高速なはずなので、ちょっとトリッキーですが

  class Fixnum
    alias each times
  end
  for i in 1000
    for j in 40000
      a[j] = 0
    end
  end

とすればもう少し速くなるのではないかと思います。少なくとも僕には、
ループカウンタが必要不可欠なら、これより速い書き方は思いつきません。

「forなんか嫌いだ!」という向きには、

  i, j = nil         # <= ここがミソ
  1000.times { |i|
    40000.times { |j|
      a[j] = 0       # <= ブロックローカルな変数は使わない
    }
  }

とすれば、ブロックローカル変数のための領域の確保が行われなくなるため
若干速くなります。

まぁ、いずれも現在の実装に依存したバッドノウハウですが、どうしても
チューニングが必要な場合等には使えるテクニックではないかと思います。


 --  ----  -     - - -- -
うえの かつひろ <unnie@blue.sky.or.jp>

In This Thread