2016-04-10 104 views
1

我有一个程序可以产生模拟输入。该程序将用户输入文件的位置和文件以及扩展名。然后使用迭代将文件分解并将其放入数组中。优化红宝石阵列或哈希

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

当程序运行时,它发送密钥到文本区经由win32ole模拟输入。

经过5000个字符后,内存开销过大,程序开始变慢。越过5000个字符,它越慢。有没有一种方法可以优化?

- 编辑 -

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'每个元素存储一个字符?你是否考虑过只存储文本,并使用'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 

的结论是:ftoa_3当GC是最快的关闭,但我会建议ftoa_4,因为它使用较少的内存,从而减少GC的时间。如果你打开GC,你会看到ftoa_4将是最快的。

从配置文件结果中,您可以看到程序在String#split中花费的大部分时间都在ftoa_1ftoa_2之间。 ftoa_1是最糟糕的,因为String#split运行多次(每行1个),并且Array.flatten!也需要很长时间。

+0

如果它变成散列,它会更快吗? –

+0

然后键值是什么,值是什么? – Aetherus

+0

值是数组中的字符,关键字将从0开始递增数字我只问这个是因为我已经读过散列最适合大规模线性搜索 –