2016-04-10 45 views
1

Simüle yazım üreten bir programım var. Program, dosyanın konumunu ve uzantı ile birlikte dosyanın bulunduğu kullanıcı girişini alır. Daha sonra dosyayı bir yineleme kullanarak bozar ve bir diziye koyar. Program çalıştığındaRuby Arrays veya Hashes'ı Optimize Etme

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

, bu win32ole yoluyla yazmaya teşvik etmek için metin alanına tuşları gönderir.

5.000 karakterden sonra fazla bellek yükü var ve program yavaşlamaya başlar. Daha geç 5000 karakter, daha yavaş gider. Bunun optimize edilebilmesi için bir yolu var mı?

--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

Doğru bir şekilde bağımsız mıyım - boş 'dizi' öğe başına bir karakter mi saklıyor? Sadece metni depolamayı ve 'each_char' kullanarak yinelemeyi düşündünüz mü? (Ya da, hiç saklamıyor, ama filin içinde bırakmıyor ...?) –

+0

@ Aetherus'un önerilerine dayanan böyle bir yöntem kullanarak alternatif bir yöntem yazdım ama bunu test ettiğimde, bellek yaklaşık 7.000 karakterler ve böylece program yavaşlamaya başladı.Şu anda bu kod snippet'inde kullandığım yöntem yaklaşık 5.000 karakterde yavaşlıyor. Bu bir gelişme, ama yeterli değil. Optimizasyon için baktığım şey sayesinde, sistem stresini yarıya indirmeyi başardım. Tam koduna buradan bakabilirsiniz. https://github.com/sinithwar/LazyProjects/blob/master/RubyTypingSim.rb –

cevap

0

Evet bu (daha az ödev ve daha az yöntem çağrıları ile yazılı sıralayıcı) optimize edilebilir: file zaten bir dize olduğundan,

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

Bu işleri ve bu nedenle, dize interpolasyonu "#{file}"'a gerek yoktur. File.read tüm dosyayı döndürür, bu her satırın üzerinde yineleme ihtiyacını ortadan kaldırır. Yineleme olmadan geçici bir empty dizisi, flatten! ve dize birleştirme <<'a gerek yoktur. Ve örnekte return explizit için gerek yoktur.


Güncelleme: performans, bellek kullanımı veya readablity: Size optimize ediyoruz ne sorudan açık değildir. Kıyaslama sonuçlarından şaştığım için kendi başıma koştum. Ve bence çözümüm seninkinden daha hızlı.

Ancak sonuçlar, farklı Ruby sürümlerinde (Ruby 2.3 kullandım) farklı olabilir, giriş dosya boyutu ve satır sayısı veya referans sayısındaki yineleme sayısı.

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

Teşekkür ederim :) Bu mükemmel bir cevaptı ve daha fazla öneri beklerken bunu hemen test edeceğim. –

+0

Tamam, ama anahtarları dosyaya göndermek için kapsayıcının üzerinde yinelemek için bir kapsayıcı olması gerekiyor. Bu, daha sonra kullanmak için bir kapsayıcıya da kaydedecek mi yoksa bir değişkene koyup, yerel kullanım için değişkeni iade etmeli mi? –

+0

Bunu test ettim ve aslında yöntemimden daha yavaş. http://pastebin.com/SWQHV6nY –

2

Ben burada kod benim kıyaslama ve profil olduğunu vermedi:

#!/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 

Ve işte benim sonucudur:

   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 

sonuca ftoa_3 en hızlı GC olduğunda olmasıdır kapatıldı, ancak daha az bellek kullandığı ve böylece GC zamanlarını azalttığı için ftoa_4'u öneririm. GC'yi açarsanız, ftoa_4'un en hızlı olacağını görebilirsiniz.

Profil sonucundan, String#split programında en çok zamanın hem ftoa_1 hem de ftoa_2'da harcadığını görebilirsiniz. ftoa_1 en kötüsüdür, çünkü String#split birçok kez (her satır için 1) çalışır ve Array.flatten! da çok zaman alır.

+0

hala daha hızlı olurdu? –

+0

Sonra tuşları ne olacağını ve değerler ne olacağını? – Aetherus

+0

değerler dizisindeki karakterler olacak ve anahtarlar 0'dan artırılmış sayılar olacaktır. Sadece bunu soruyorum çünkü karmaların büyük lineer aramalar için en iyisi olduğunu okumuştum –