• ベストアンサー

Rubyにおける処理時間

皆様のお陰で、Rubyを実践的に理解し、仕事に活用しております。 ついでに、RubyとPerlの比較(処理時間)をしました。 同じデータを同じロジック作成されたそれぞれのスクリプトで処理しました。 スクリプトの作り方にも、問題はありますが、ざっくり、処理時間は、 Rubyは、Perlの倍の時間を要しているようです。 (Perlでは、2分、Rubyでは4分) こんなもんなのでしょうか? Rubyで、処理速度を上げる方法はあるのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

プロファイルの結果の見方ですが詳しくは Rubyリファレンスマニュアル - profile http://www.ruby-lang.org/ja/man/?cmd=view;name=profile を参照してください。 ProfilerではなくProfileですが、フォーマットは同一です。 Profilerのページはこちらに Rubyリファレンスマニュアル - profiler http://www.ruby-lang.org/ja/man/?cmd=view;name=profiler 簡単には time seconds seconds calls ms/call ms/call name 0.00 31.69 0.00 1 0.00 31688.00 #toplevel 一番下の行の左から二番目の数字が プロファイリングした期間の所要時間です。 31.69 43.63 31.78 96.72 ← 計測間違い。正しくは60s前後の数字になります。 質問者さんのスクリプトでは正規表現の部分をいじっただけで 速度が大幅に変わったとのことでしたので、 正規表現について get_profile("fixed pattern") { |l| l.each{ |x| /^\w+\s*$/ =~ x} } get_profile("variable interpolation ") { |l| l.each{ |x| /#{patstr}/ =~ x} } get_profile("variable interpolation with o flag") { |l| l.each{ |x| /#{patstr}/o =~ x} } get_profile("Regexp object") { |l| l.each{ |x| pat.match(x)} } ・単純な固定パターン ・変数展開を含んだパターン ・変数展開にoフラグをつけたパターン ・正規表現オブジェクトを使用したパターン について計測しています。 結果として 31.69 固定パターン 43.63 変数展開使用 31.73 oフラグつき変数展開 96.72 正規表現オブジェクト使用 (計測間違い。正しくは60s前後の数字になります) となっており、変数展開は遅くなる要因であることが伺えます。 また、oフラグをつければ、変数展開を使っても 変数展開なしとほぼ同じ速度らしいと判断できます #しかしなんで正規表現オブジェクトがこんなに遅いのだろう? ただこの数字からはとても桁が変わるほどの影響が出るようには思えないのですが。 質問者さんがご自分のスクリプトのプロファイリングをしたいということでしたら、 一番単純なのは、オプションなどの処理を終えた メインの処理の始まりのところで Profiler__.start_profile を その処理が終わったところで Profiler__.print_profile(STDOUT) とすればよろしいのではないでしょうか。 プロファイリングしているときは当然ながら実行速度が 格段に落ちますのでご注意くださいませ。 それと >宜しく、ご教授願います。 このような場合には「教示」を使うべきです。

noname#95859
質問者

お礼

sakusaker7さん、ありがとうございます。 また、新しい世界に踏み込んだような気がします。 一気に理解はできませんが、気長にやっていきます。 ありがとうございます。 また、言葉遣いの件、気をつけます。

その他の回答 (4)

回答No.4

> こんなもんなのでしょうか? 現在のRubyインタプリタの実装では、 こんなもんと思います。参考URLでは、 短いサンプルプログラムのベンチマークによる比較 ではありますが、殆どのケースでRubyはPerlより 遅いことがわかります。 > Rubyで、処理速度を上げる方法はあるのでしょうか? 他の方が示されているように、プロファイルを取って 遅いところを高速化するのはRubyに限らず有効な手段ですし、 インタプリタ特有の高速化テクニックも存在するようですが、 いずれも手でプログラムを書き換える必要があります。 後はインタプリタの改良を待つしかないのですが、 現在開発中の Ruby 1.9 (「安定志向版」のRuby 1.9.1は今年12月リリース予定) は、かなり高速なインタプリタが搭載されるので、それを 期待するのもいいかもしれません。

参考URL:
http://shootout.alioth.debian.org/gp4/benchmark.php?test=all&lang=perl&lang2=ruby
noname#95859
質問者

お礼

jyufi_februaryさん、ありがとうございます。 Rubyがこんなに遅いとは、ちょっと意外でした。 しかし、Ruby 1.9に期待します。 新しい情報ありがとうございました。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.3

一般論で申し上げれば、Rubyは同様の処理をする他の言語(Perl、Pythonなど) に比べると処理速度は遅い傾向にあります。 特に正規表現に関する部分は、Perlでは非常に手が入れられている部分で 色々高速化のための努力が払われています。 Rubyスクリプトを早く動作させるためのコツというものもあるにはありますが 今回はとりあえずそれは置いといて、 require 'profiler' def get_profile(m) puts m testlines = <<EOS.split("\n") xxxxxxxxxx yyyyyyyyyy zzzzzzzzzz 1111111111 aaaaaaaaa, bbbbbbbbb EOS Profiler__.start_profile 1.upto(100000) do |i| yield testlines end Profiler__.print_profile(STDOUT) end patstr = '^\w+\s*$' pat = Regexp.new("#{patstr}") get_profile("fixed pattern") { |l| l.each{ |x| /^\w+\s*$/ =~ x} } get_profile("variable interpolation ") { |l| l.each{ |x| /#{patstr}/ =~ x} } get_profile("variable interpolation with o flag") { |l| l.each{ |x| /#{patstr}/o =~ x} } get_profile("Regexp object") { |l| l.each{ |x| pat.match(x)} } こんな感じに簡単にプロファイリングしてみた結果が以下です。 fixed pattern % cumulative self self total time seconds seconds calls ms/call ms/call name 73.31 23.23 23.23 100000 0.23 0.23 Array#each 26.69 31.69 8.46 1 8457.00 31688.00 Integer#upto 0.00 31.69 0.00 1 0.00 31688.00 #toplevel variable interpolation % cumulative self self total time seconds seconds calls ms/call ms/call name 81.41 35.52 35.52 100000 0.36 0.36 Array#each 18.59 43.63 8.11 1 8109.00 43625.00 Integer#upto 0.00 43.63 0.00 1 0.00 43625.00 #toplevel variable interpolation with o flag % cumulative self self total time seconds seconds calls ms/call ms/call name 63.22 20.09 20.09 100000 0.20 0.20 Array#each 36.78 31.78 11.69 1 11688.00 31782.00 Integer#upto 0.00 31.78 0.00 1 0.00 31782.00 #toplevel Regexp object % cumulative self self total time seconds seconds calls ms/call ms/call name 56.56 54.71 54.71 100000 0.55 0.85 Array#each 31.11 84.80 30.09 600000 0.05 0.05 Regexp#match 12.32 96.72 11.92 1 11920.00 96719.00 Integer#upto 0.00 96.72 0.00 1 0.00 96719.00 #toplevel 一応一般的な傾向は読み取れるのではないかと思います。 質問者さんが#2の補足で述べられた十倍強の比率には なりませんが、 ナイーブに #{}による変数展開を使った正規表現マッチングは 時間が掛かっています。

noname#95859
質問者

補足

sakusaker7さん、ありがとうございます。 示してくださったサンプルは、小生の環境で、うまく動きました。 しかしながら、profiler、スクリプトを理解しようと努めましたが、まだよく分かっておりません。 Rubyリファレンスマニュアルでは、"profile は各メソッドの実行時間に関する統計を出力”とあります。 require 'profiler' ---ライブラリ呼び出し def get_profile(m) -------関数定義 Profiler__.start_profile--ライブラリ「profiler」に対してスタート指示 1.upto(100000) do |i| -----よく分かりません Profiler__.print_profile(STDOUT) ---ライブラリ「profiler」への指示 主たる検証内容------------------------------------------------------ get_profile("fixed pattern") { |l| l.each{ |x| /^\w+\s*$/ =~ x} } get_profile("variable interpolation ") { |l| l.each{ |x| /#{patstr}/ =~ x} } get_profile("variable interpolation with o flag") { |l| l.each{ |x| /#{patstr}/o =~ x} } get_profile("Regexp object") { |l| l.each{ |x| pat.match(x)} } 主たる検証内容------------------------------------------------------ 仮に小生のスクリプトを使って、実行時間に関して統計を取ろうとした場合、どのようにすれば良いですか? それと、具体的に、出力をどのように、理解すればよいのか、解説していただけないでしょうか? # % cumulative self self total # time seconds seconds calls ms/call ms/call name # 60.49 122.90 122.90 100000 1.23 1.76 Array#each # 25.91 175.56 52.66 600000 0.09 0.09 Regexp#match # 13.60 203.19 27.64 1 27636.00 203193.00 Integer#upto # 0.00 203.19 0.00 1 0.00 203193.00 #toplevel 処理時間は、203.19秒。Array#eachが全体の所要時間の60.49%を占めており、その時間は203.19秒である。 Array#eachは、呼出回数が、100000であったので、1回当たりは、1.23msecである。 total ms/callがよくわかりません。 ここから、何を掴めばよいのか、まだよく分かりません。 誠に、申し訳ありませんが、宜しく、ご教授願います。

回答No.2

Perl もインタプリタです。 まず、どこで時間を喰っているのかプロファイルする必要があります。 単純に言語比較はできないでしょう。

noname#95859
質問者

補足

shuujin01さん、ありがとうございます。 >まず、どこで時間を喰っているのかプロファイルする必要があります。 以下のような、内容で足りるでしょうか? 対象フォルダに存在するファイル数:45個 ファイルサイズ:500Kbyteから5MByte/file 対象行総数-----------------3840000行 処理内容-------------------テキスト処理 小生、最初に、Perlにて、スクリプトを組みました。 次に、アルゴリズムはそのままに、RUBYで書き換えました。 経験的に、RUBYでは、正規表現の中に、変数を持ち込むと、処理時間が長くなるのに気がついております。 そこで、意図的に、これを実施してみました。 (/^ABC\s*$/=~text) && (myFlabA==1) && (myFlabA=0) この部分(スクリプトの中に6箇所)を、次のように書き換えました。 myITEM="ABC" (/^#{myITEM}\s*$/=~text) 0から、6箇所すべてを置き換えて、処理時間を記録しました。 結果は、以下の通りです。 置換数-- 処理時間 0 --- 0:04:17 (4分17秒) 1 --- 0:09:14 2 --- 0:14:29 3 --- 0:20:45 4 --- 0:28:16 5 --- 0:45:30 6 --- 0:50:00 (50分00秒) この実験から、基本的に、スクリプトのアルゴリズムは、関係ないと考えます。 むしろ、RUBYであるが故の、組み方の問題、例えば、上記のような、 正規表現の中に、変数を持ち込む際には、なるべく、数を減らすか、パターンをイミーディエートで記述する、ということであろうと、考えます。 小生の理解したことは、ここまでです。 宜しく、サジェスチョンをお願いいたします。

回答No.1

Rubyはインタプリタ言語だったと記憶しています。 故に実行形式のPerlより処理速度は落ちます。 処理速度を上げるなら、アルゴリズムを変えるしかないでしょう。