2016-04-10 6 views
1

私はシミュレートされた型を生成するプログラムを持っています。プログラムは、ファイルの場所がどこにあるか、拡張子と共にファイルをユーザー入力します。次に、反復を使用してファイルを分割し、配列に格納します。Ruby配列やハッシュを最適化する

def file_to_array(file) 
    empty = [] 
    File.foreach("#{file}") do |line| 
    empty << line.to_s.split('') 
    end 
    return empty.flatten! 
end 

プログラムが実行されると、それはwin32oleを経由して入力してシミュレートするために、テキスト領域にキーを送信します。

5,000文字を超えると、メモリオーバーヘッドが大きくなりすぎ、プログラムが遅くなります。さらに過去5,000文字ほど、遅くなります。これを最適化する方法はありますか?

--EDIT--

require 'Benchmark' 

def file_to_array(file) 
    empty = [] 
    File.foreach(file) do |line| 
    empty << line.to_s.split('') 
    end 
    return empty.flatten! 
end 
def file_to_array_2(file) 
    File.read(file).split('') 
end 

file = 'xxx' 

Benchmark.bm do |results| 
    results.report { print file_to_array(file) } 
    results.report { print file_to_array_2(file) } 
end 
    user  system  total  real 
0.234000 0.000000 0.234000 ( 0.787020) 
0.218000 0.000000 0.218000 ( 1.917185) 
+0

私は正しく行き渡っていません - 配列 'empty'は要素ごとに1文字を格納しますか?あなたは単にテキストを保存し、 'each_char'を使ってそれを繰り返すことを考えましたか? (または、それをまったく保管していないのですが...?) –

+0

@ Aetherusの提案に基づいてこのようなメソッドを使って代替メソッドを書いたが、テストしたところ、メモリは約7,000プログラムが遅くなり始めた。このコードスニペットで現在使用している方法は、約5,000文字になります。改善ですが、十分ではありません。私が最適化のために見てきたことを通して、私はシステムのストレスを半減させました。ここで完全なコードを見ることができます。 https://github.com/sinithwar/LazyProjects/blob/master/RubyTypingSim.rb –

答えて

0

はい、これは(それほど割り当てと以下のメソッド呼び出しで、書かれたソーター)を最適化することができます。

def file_to_array(file) 
    File.read(file).split('') 
end 

これは動作しますが、fileはすでに文字列であるため、したがって、文字列補間"#{file}"の必要はありません。 File.readはファイル全体を返します。これにより、各行を繰り返し処理する必要がなくなります。反復処理がなければ、一時的なempty配列、flatten!および文字列連結<<の必要はありません。あなたの例では、解読の必要はありませんreturnです。


更新:パフォーマンス、メモリ使用量や可読性:あなたのために最適化されているものをあなたの質問から明らかではありません。あなたのベンチマークの結果に驚いたので、私は自分自身を走らせました。私のソリューションはあなたのものよりも速いと思います。

しかし、Rubyのバージョン(Ruby 2.3を使用)によって結果が異なる場合があります。入力ファイルのサイズと行数または繰り返し回数がベンチマークで実行されました。

def file_to_array_1(file) 
    empty = [] 
    File.foreach("#{file}") do |line| 
    empty << line.to_s.split('') 
    end 
    return empty.flatten! 
end 

def file_to_array_2(file) 
    File.read(file).split('') 
end 

require 'benchmark' 

# file = '...' # a path to a file with about 26KB data in about 750 lines 
n = 1000 

Benchmark.bmbm(15) do |x| 
    x.report("version 1 :") { n.times do; file_to_array_1(file); end } 
    x.report("version 2 :") { n.times do; file_to_array_2(file); end } 
end 

# Rehearsal --------------------------------------------------- 
# version 1 :  11.970000 0.110000 12.080000 (12.092841) 
# version 2 :  8.150000 0.120000 8.270000 ( 8.267420) 
# ----------------------------------------- total: 20.350000sec 

#      user  system  total  real 
# version 1 :  11.940000 0.100000 12.040000 (12.045505) 
# version 2 :  8.130000 0.110000 8.240000 ( 8.248707) 
# [Finished in 40.7s] 
+0

ありがとうございました:)これはすばらしい答えでした。私はさらに多くの提案を待つのですぐにテストします。 –

+0

さて、キーをファイルに送るためにコンテナを反復処理するには、コンテナにする必要があります。これは後で使用するためにコンテナにも保存されますか?それを変数に入れ、変数をローカルで使用する必要がありますか? –

+0

私はそれをテストしましたが、実際に私の方法よりも遅いです。 http://pastebin.com/SWQHV6nY –

2

私はここに、私のベンチマークとプロファイルをしたコードです:

#!/usr/bin/env ruby 
require 'benchmark' 
require 'rubygems' 
require 'ruby-prof' 

def ftoa_1(path) 
    empty = [] 
    File.foreach(path) do |line| 
    empty << line.to_s.split('') 
    end 
    return empty.flatten! 
end 

def ftoa_2(path) 
    File.read(path).split('') 
end 

def ftoa_3(path) 
    File.read(path).chars 
end 

def ftoa_4(path) 
    File.open(path) { |f| f.each_char.to_a } 
end 

GC.start 
GC.disable 

Benchmark.bm(6) do |x| 
    1.upto(4) do |n| 
    x.report("ftoa_#{n}") {send("ftoa_#{n}", ARGV[0])} 
    end 
end 

1.upto(4) do |n| 
    puts "\nProfiling ftoa_#{n} ...\n" 

    result = RubyProf.profile do 
    send("ftoa_#{n}", ARGV[0]) 
    end 

    RubyProf::FlatPrinter.new(result).print($stdout) 
end 

そして、ここでは私の結果である:

   user  system  total  real 
ftoa_1 2.090000 0.160000 2.250000 ( 2.250350) 
ftoa_2 1.540000 0.090000 1.630000 ( 1.632173) 
ftoa_3 0.420000 0.080000 0.500000 ( 0.505286) 
ftoa_4 0.550000 0.090000 0.640000 ( 0.630003) 

Profiling ftoa_1 ... 
Measure Mode: wall_time 
Thread ID: 70190654290440 
Fiber ID: 70189795562220 
Total: 2.571306 
Sort by: self_time 

%self  total  self  wait  child  calls name 
83.39  2.144  2.144  0.000  0.000 103930 String#split 
12.52  0.322  0.322  0.000  0.000  1 Array#flatten! 
    3.52  2.249  0.090  0.000  2.159  1 <Class::IO>#foreach 
    0.57  0.015  0.015  0.000  0.000 103930 String#to_s 
    0.00  2.571  0.000  0.000  2.571  1 Global#[No method] 
    0.00  2.571  0.000  0.000  2.571  1 Object#ftoa_1 
    0.00  0.000  0.000  0.000  0.000  1 Fixnum#to_s 

* indicates recursively called methods 

Profiling ftoa_2 ... 
Measure Mode: wall_time 
Thread ID: 70190654290440 
Fiber ID: 70189795562220 
Total: 1.855242 
Sort by: self_time 

%self  total  self  wait  child  calls name 
99.77  1.851  1.851  0.000  0.000  1 String#split 
    0.23  0.004  0.004  0.000  0.000  1 <Class::IO>#read 
    0.00  1.855  0.000  0.000  1.855  1 Global#[No method] 
    0.00  1.855  0.000  0.000  1.855  1 Object#ftoa_2 
    0.00  0.000  0.000  0.000  0.000  1 Fixnum#to_s 

* indicates recursively called methods 

Profiling ftoa_3 ... 
Measure Mode: wall_time 
Thread ID: 70190654290440 
Fiber ID: 70189795562220 
Total: 0.721246 
Sort by: self_time 

%self  total  self  wait  child  calls name 
99.42  0.717  0.717  0.000  0.000  1 String#chars 
    0.58  0.004  0.004  0.000  0.000  1 <Class::IO>#read 
    0.00  0.721  0.000  0.000  0.721  1 Object#ftoa_3 
    0.00  0.721  0.000  0.000  0.721  1 Global#[No method] 
    0.00  0.000  0.000  0.000  0.000  1 Fixnum#to_s 

* indicates recursively called methods 

Profiling ftoa_4 ... 
Measure Mode: wall_time 
Thread ID: 70190654290440 
Fiber ID: 70189795562220 
Total: 0.816140 
Sort by: self_time 

%self  total  self  wait  child  calls name 
99.99  0.816  0.816  0.000  0.000  2 IO#each_char 
    0.00  0.000  0.000  0.000  0.000  1 File#initialize 
    0.00  0.000  0.000  0.000  0.000  1 IO#close 
    0.00  0.816  0.000  0.000  0.816  1 <Class::IO>#open 
    0.00  0.000  0.000  0.000  0.000  1 IO#closed? 
    0.00  0.816  0.000  0.000  0.816  1 Global#[No method] 
    0.00  0.816  0.000  0.000  0.816  1 Enumerable#to_a 
    0.00  0.816  0.000  0.000  0.816  1 Enumerator#each 
    0.00  0.816  0.000  0.000  0.816  1 Object#ftoa_4 
    0.00  0.000  0.000  0.000  0.000  1 Fixnum#to_s 

* indicates recursively called methods 

結論はGCがあるときftoa_3が最速であるということです私はftoa_4を推奨します。なぜなら、メモリの使用量が少なくてGCの時間が短くなるからです。 GCをオンにすると、ftoa_4が最も速く表示されます。

プロファイル結果から、String#splitには、ほとんどの時間がftoa_1ftoa_2であることがわかります。 ftoa_1は、String#splitが何度も実行されるため(各行に1つ)、最悪です。Array.flatten!にも多くの時間がかかります。

+0

ハッシュに変換されても速くなるでしょうか –

+0

鍵となるものとその値は何ですか? – Aetherus

+0

値は配列内の文字になります。キーは0からインクリメントされます。私は大量の線形検索にハッシュが最適であることを読んだので、これを尋ねます。 –

関連する問題