2017-10-19 5 views
1

Je cherche à utiliser plusieurs threads pour accélérer un processus qui est très E/S lié. Je veux être en mesure d'appeler un service de repos avec des ID pris à partir d'un fichier CSV en boucle. Ce que je n'ai pas réussi à comprendre est comment séparer élégamment le fichier en morceaux en fonction de la quantité de threads que je veux utiliser.Ruby: Split et lire une partie d'un fichier En fonction de discussion Count

Le fichier CSV contient une seule colonne de ids comme ceci: ...

require 'benchmark' 
require 'csv' 

FILE_RECORD_COUNT = File.open("path-to-csv","r").readlines.size 

def setup(thread_count) 
    threads = [] 
    thread_count.times do 
    threads << Thread.new do 
     fetches_per_thread = FILE_RECORD_COUNT/thread_count 

     fetches_per_thread.times do 
     CSV.foreach("id_file.csv") do |id| 
      response = RestClient.get("https://api.examplerest/names?id={#id}",{accept: :json}) 
      # do some quick validation... 
     end 
     end 
    end 
    end 

    threads.each(&:join) 
end 

def run_benchmark 
    Benchmark.bm(20) do |bm| 
    [1, 2, 3, 5, 6, 10, 15, 30, 100].each do |thread_count| 
     bm.report("with #{thread_count} threads") do 
     setup(thread_count) 
     end 
    end 
    end 
end 

Là où je suis perplexe, et où je besoin d'une solution est le bloc de code CSV.foreach(id_file.csv") do |id|.... Je voudrais diviser les données dynamiquement et les nourrir dans chaque thread, puis faire un appel de repos. Je sais que je pourrais diviser le fichier manuellement, mais je voudrais éviter cela.

J'ai essayé de faire cela en un point de référence à partir d'un exemple, je trouve en ligne, pour voir où le sweet spot est pour le nombre de threads.

EDIT: En utilisant la réponse de BernardK, je suis en mesure d'exécuter mon code enfilée et je revins les résultats suivants:

     | user | system | total | real | 
with 1 threads   5.125000 2.594000 7.719000 (40.416162) 
with 2 threads   1.625000 2.015000 3.640000 (28.571521) 
with 3 threads   1.578000 1.625000 3.203000 (17.210526) 
with 4 threads   1.578000 1.235000 2.813000 ( 8.496068) 
with 5 threads   1.406000 1.250000 2.656000 ( 6.779216) 
with 10 threads  1.875000 1.328000 3.203000 ( 5.069487) 
with 15 threads  2.016000 1.640000 3.656000 ( 4.285426) 
with 30 threads  2.125000 1.625000 3.750000 ( 3.817084) 
with 100 threads  2.281000 1.375000 3.656000 ( 3.943304) 

Ce fut un essai, mais montre vraiment comment les discussions comme celles-ci peuvent speedup Code Ruby!

+0

S'il vous plaît attendre, il y a une erreur ('@ lines.each_slice' doit remplacer' thread_count.times'). – BernardK

+0

@BernardK, d'accord, quand vous êtes-post, je vais mettre en œuvre vos modifications et vous faire savoir comment ça se passe. –

+0

Terminé. (la version précédente lisait le fichier 'thread_count' fois) – BernardK

Répondre

1

Le fichier est lu dans un tableau, qui est divisé en morceaux en utilisant Enumerable#each_slice.

require 'benchmark' 
require 'csv' 

@file_name = 'xxx.txt' 
file = File.open(@file_name, 'w') 
1000.times do | i | 
    file.puts "#{i.to_s}" 
end 
file.close 

@lines = [] 
CSV.foreach(@file_name) { | line | @lines << line } 
FILE_RECORD_COUNT = @lines.size 
puts FILE_RECORD_COUNT 

def setup(thread_count) 
    puts "----- thread_count=#{thread_count}" 
    threads = [] 
    fetches_per_thread = FILE_RECORD_COUNT/thread_count 
    puts "----- fetches_per_thread=#{fetches_per_thread}" 
    raise 'invalid slice size' if fetches_per_thread < 1 

    @lines.each_slice(fetches_per_thread) do | slice | 
    threads << Thread.new do 
     puts "===== slice from #{slice.first} to #{slice.last}" 
     slice.each do | id | 
#  puts id 
#   response = RestClient.get("https://api.examplerest/names/{#id}",{accept: :json}) 
      # do some quick validation... 
     end # slice.each 
    end # Thread.new 
    end # @lines.each_slice 

    threads.each(&:join) 
end # def setup 

def run_benchmark 
    Benchmark.bm(20) do |bm| 
    [1, 2, 3, 5, 6, 10, 15, 30, 100].each do |thread_count| 
     bm.report("with #{thread_count} threads") do 
     setup(thread_count) 
     end 
    end 
    end 
end 

run_benchmark 

Exécution:

$ -------------------------------- 
-bash: --------------------------------: command not found 
$ ruby -w t.rb 
1000 
          user  system  total  real 
with 1 threads  ----- thread_count=1 
----- fetches_per_thread=1000 
===== slice from ["0"] to ["999"] 
    0.000000 0.000000 0.000000 ( 0.000288) 
with 2 threads  ----- thread_count=2 
----- fetches_per_thread=500 
===== slice from ["0"] to ["499"] 
===== slice from ["500"] to ["999"] 
    0.000000 0.000000 0.000000 ( 0.000318) 
with 3 threads  ----- thread_count=3 
----- fetches_per_thread=333 
===== slice from ["0"] to ["332"] 
===== slice from ["666"] to ["998"] 
===== slice from ["999"] to ["999"] 
===== slice from ["333"] to ["665"] 
    0.000000 0.000000 0.000000 ( 0.000549) 
with 5 threads  ----- thread_count=5 
----- fetches_per_thread=200 
===== slice from ["0"] to ["199"] 
===== slice from ["200"] to ["399"] 
===== slice from ["400"] to ["599"] 
===== slice from ["600"] to ["799"] 
===== slice from ["800"] to ["999"] 
    0.000000 0.000000 0.000000 ( 0.000536) 
with 6 threads  ----- thread_count=6 
----- fetches_per_thread=166 
===== slice from ["166"] to ["331"] 
===== slice from ["664"] to ["829"] 
===== slice from ["830"] to ["995"] 
===== slice from ["996"] to ["999"] 
===== slice from ["0"] to ["165"] 
===== slice from ["332"] to ["497"] 
===== slice from ["498"] to ["663"] 
    0.000000 0.000000 0.000000 ( 0.000735) 
with 10 threads  ----- thread_count=10 
----- fetches_per_thread=100 
===== slice from ["900"] to ["999"] 
... 
===== slice from ["190"] to ["199"] 
===== slice from ["200"] to ["209"] 
===== slice from ["210"] to ["219"] 
===== slice from ["220"] to ["229"] 
===== slice from ["230"] to ["239"] 
===== slice from ["240"] to ["249"] 
... 
===== slice from ["970"] to ["979"] 
===== slice from ["980"] to ["989"] 
===== slice from ["990"] to ["999"] 
===== slice from ["20"] to ["29"] 
===== slice from ["30"] to ["39"] 
    0.000000 0.000000 0.000000 ( 0.011656) 

Puis-je utiliser la commande find dans le terminal pour trouver -------------------------------------- et sauter au début de l'exécution.

+0

Votre benchmark ne montre-t-il pas que plus de threads le ralentissent? – Max

+0

@Max N'est-ce pas une question pour Austin L? C'est son benchmark (montrant le meilleur temps par 5 threads). Je viens d'aider à diviser le fichier CSV en morceaux. – BernardK