我正在尝试使用Celluloid异步处理一些.csv数据。我已经读过,使用期货可以让你在主线程终止之前等待一个actor池完成。我看过some examples证明了这一点。
然而,当我在我的示例代码中实现它时,事实证明使用future并不比同步处理更快。谁能看到我做错了什么?
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完成]
答案 0 :(得分:2)
您使用的是标准的红宝石核磁共振成像解释器吗?
如果是这样,您将无法获得完全由CPU限制的任务的任何加速 - 即,不执行任何I / O但完全在CPU中进行计算的任务。你的测试' 100000.times {|n| n}
的任务确实完全受CPU限制。
因为MRI解释器具有全局解释器锁定功能,因此您通过多线程获得任何加速以获得完全由CPU限制的任务的原因是因为MRI解释器具有全局解释器锁定功能。 (GIL),它可以防止ruby解释器同时使用多个CPU核心。像赛璐珞这样的多线程并行性可以通过在多核系统上同时运行不同的CPU核心来加速CPU工作,就像现在的大多数系统一样。
但在核磁共振成像中,这是不可能的。这是红宝石MRI解释器的限制。
如果您安装JRuby并在JRuby下运行测试,您应该看到加速。
如果您的任务涉及一些I / O(如进行数据库查询,或等待远程HTTP API,或执行重要的文件读取或写入),您还可以看到一些速度在MRI下进行。您的任务花费在I / O上的比例时间越多,速度就越快。这是因为即使MRI不允许线程在多个CPU核心上同时执行,仍然可以切换等待I / O的线程并切换另一个线程来工作。如果你没有使用线程,程序就会坐在等待I / O不工作的地方。
如果你google for" ruby GIL"你可以找到更多关于这个问题的讨论。
如果您真正从事CPU密集型工作,可以从一种对您的程序有重大帮助的方式从多线程并行性中受益,请考虑切换到Jruby。
如果你确实需要多线程并行性,使用Celluloid的替代方法是使用concurrent-ruby包中的Futures或Promises。并发红宝石内部通常比赛璐珞更简单,重量更轻。但是,无论使用哪种工具,编写多线程代码都很棘手,即使使用Celluloid或ruby-concurrent来提供比直接使用线程更高级别的抽象,使用多线程并发也需要熟悉一些技术,并不时需要一些棘手的调试。