2015-10-13 28 views
1

我正在嘗試使用賽璐珞異步處理一些.csv數據。我讀過使用期貨使您能夠在主線程終止之前等待一組角色完成。我看過some examples,證明了這一點。賽璐珞期貨不會比同步計算快嗎?

但是,當我在我的示例代碼中實現它時,事實證明使用futures並不比同步處理更快。任何人都可以看到我做錯了什麼?

require 'smarter_csv' 
require 'celluloid/current' 
require 'benchmark' 

class ImportActor 
    include Celluloid 

    def process_row(row) 
    100000.times {|n| n} 
    end 

end 

def do_all_the_things_with_futures 
    pool = ImportActor.pool(size: 10) 
    SmarterCSV.process("all_the_things.csv").map do |row| 
    pool.future(:process_row,row) 
    end.map(&:value) 
end 


def do_all_the_things_insync 
    pool = ImportActor.pool(size: 10) 
    SmarterCSV.process("all_the_things.csv") do |row| 
    pool.process_row(row) 
    end 
end 

puts Benchmark.measure { do_all_the_things_with_futures} 
puts Benchmark.measure { do_all_the_things_insync } 

2.100000 0.030000 2.130000(2.123381)

2.060000 0.020000 2.080000(2.069357)

[完成了4.6S]

回答

2

您使用的是標準的Ruby解釋器MRI?

如果是這樣,那麼對於完全受CPU限制的任務(即沒有執行任何I/O,但完全在CPU中進行計算的任務),不會得到任何加速。您的「測試」任務100000.times {|n| n}確實完全受CPU限制。

您不能通過多線程完成CPU上完全受CPU限制的任務而獲得任何加速的原因是,MRI解釋器具有「全局解釋器鎖定」(GIL),可防止多個你的CPU核心不會被ruby解釋器使用。多線程並行,就像賽璐珞給你一樣,只需在不同的CPU內核上同時運行不同的線程,就可以加快CPU的工作速度,就像現在大多數系統一樣,在多核系統上。

但在MRI中,這是不可能的。這是ruby MRI解釋器的限制。

如果您安裝JRuby並在JRuby下運行您的測試,您應該會看到加速。如果您的任務涉及一些I/O(如進行數據庫查詢,或等待遠程HTTP API,或者執行大量的文件讀取或寫入操作),您還可以在MRI下看到一些加速。任務花費在I/O上的時間越多,速度越快。這是因爲即使MRI不允許線程在多個CPU內核上同時執行,等待I/O的線程仍然可以切換出來,並且另一個線程切換到工作狀態。而如果你不使用線程,那麼程序只是坐在等待I/O不工作。

如果你是谷歌的「紅寶石吉爾」,你可以找到更多的討論這個問題。

如果您真的在做一些能夠顯着幫助您的程序的方式從多線程並行性中受益的CPU密集型工作,請考慮切換到Jruby。

如果您確實需要多線程並行性,則使用Celluloid的替代方法是使用concurrent-ruby包中的Futures或Promises。併發紅寶石比賽璐珞通常更簡單,重量更輕。但是,無論使用哪種工具,編寫多線程代碼都會非常棘手,即使使用賽璐珞或ruby-concurrent爲您提供比直接使用線程更高級別的抽象,但使用多線程併發將需要熟悉一些這樣的技術,並且不時需要一些棘手的調試。

+0

添加'睡眠1'而不是我最初的迭代有訣竅。異步方法的速度提高了10倍。令人驚訝的是,徹底的答案。乾杯。 –

+0

很高興它有幫助;安裝jruby並不難(如果使用rvm,或者我建議使用ruby-build和chruby);如果您在JRuby下嘗試您的原始示例,您應該看到顯着加速!除非您的計算機上有10個CPU核心,否則不是10x,而是顯着的。嘗試和比較會很有趣。 – jrochkind