Ruby多线程计时与cpu内核计数

Ruby多线程计时与cpu内核计数,ruby,multithreading,fork,cpu,Ruby,Multithreading,Fork,Cpu,我编写了以下代码来测试Ruby中的单线程和多线程。我循环一个包含两个元素的数组,并按顺序休眠1秒(执行时间约为2秒)。然后我会做同样的事情,但每次都会分叉进程(因为两个sleep命令并行运行,所以执行时间约为1秒)。这对我来说是有道理的 当我增加数组的大小,即更多的迭代时,我注意到多线程总是在~1秒内返回。我在VirtualBox上的Ubuntu实例上运行,有1个处理单元可供我使用: root@vbox-docker:~/docker-projects/hello-world/lib# npro

我编写了以下代码来测试Ruby中的单线程和多线程。我循环一个包含两个元素的数组,并按顺序休眠1秒(执行时间约为2秒)。然后我会做同样的事情,但每次都会分叉进程(因为两个sleep命令并行运行,所以执行时间约为1秒)。这对我来说是有道理的

当我增加数组的大小,即更多的迭代时,我注意到多线程总是在~1秒内返回。我在VirtualBox上的Ubuntu实例上运行,有1个处理单元可供我使用:

root@vbox-docker:~/docker-projects/hello-world/lib# nproc
1
我的假设是,随着迭代次数的增加,由于消耗了所有的处理单元,多线程将花费更长的时间。我唯一能想到的另一件事是,时钟速度会同时推动许多线程,我需要进一步增加迭代次数,以便看到多线程方面的速度减慢

我的问题是:我的想法正确吗?我的逻辑有道理吗?这是查看单线程应用程序与多线程应用程序结果差异的准确测试吗

检查下面的Ruby脚本,如果您选择运行,请提供反馈

提前谢谢

Edit:将迭代设置为500,睡眠设置为5秒,导致多线程部分需要22秒才能更改。有趣。

#!/usr/bin/ruby

#Do a single threaded run, timing each iteration of the array [0,1], doing two iterations because of two elements
before = Time.now
[0,1].each do |i|
  sleep 1
end
after = Time.now
delta = (after - before) * 1000.0
puts "Single-threaded: #{delta} milliseconds" #Should be 1000 miliseconds X number of elements in the array (2)

#Do a multi threaded run, timing each iterationg of the array [0,1], doing two iterations because of the two elements
before = Time.now
[0,1].each do |i|
  fork do #Instead of running each operation sequentially, run each process in a different thread
    sleep 1
  end
end
Process.waitall
after = Time.now
delta = (after - before) * 1000.0
puts "Multi-threaded: #{delta} milliseconds" #Should be 1000 miliseconds X 1 while array length < CPU Core count
#/usr/bin/ruby
#执行单线程运行,对数组[0,1]的每次迭代进行计时,因为有两个元素而执行两次迭代
以前=时间。现在
[0,1]。每个do|i|
睡眠1
结束
之后=时间。现在
增量=(之后-之前)*1000.0
puts“单线程:#{delta}毫秒”#应为1000毫秒X数组中的元素数(2)
#执行多线程运行,为数组[0,1]的每次迭代计时,由于这两个元素,执行两次迭代
以前=时间。现在
[0,1]。每个do|i|
fork do#不是按顺序运行每个操作,而是在不同的线程中运行每个进程
睡眠1
结束
结束
Process.waitall
之后=时间。现在
增量=(之后-之前)*1000.0
放置“多线程:{delta}毫秒”#应为1000毫秒X 1,而数组长度
要测量并发性,您需要做一些工作。下面是一个计算Fibbinaci序列的实现(以一种故意缓慢的方式)

这是意料之中的——Ruby使用绿色线程,这意味着单个Ruby进程一次不能消耗超过1个CPU核。我的机器有两个内核,所以在分叉时运行速度大约是原来的两倍(允许分叉开销)

如果我在JRuby下运行它,它确实有本机线程(即,实际的进程内并发),我会得到如下结果:

$ ruby fib.rb
                 user       system     total    real
Single threaded  4.050000   0.000000   4.050000 (  4.054188)
Multithreaded    4.100000   0.000000   4.100000 (  4.114595)
Forked           0.000000   0.000000   4.000000 (  2.054361)
$ ruby fib.rb
                user        system    total     real
Single threaded 27.850000   0.100000  27.950000 ( 27.812978)
Multithreaded   27.870000   0.060000  27.930000 ( 14.355506)
进程内线程确实将任务的运行时间减半(不过,yikes,这似乎是JRuby特别不擅长的一个)


您可能会想,如果Ruby不能在每个进程中使用多个内核,为什么它会提供线程——这是因为在等待IO时,您仍然可以跨线程工作!如果您的应用程序花费大量时间与网络套接字通信(例如进行数据库查询),那么您将看到多线程在阻塞套接字时让其他线程工作,从而提高并发性。

要测量并发性,您需要做些工作。下面是一个计算Fibbinaci序列的实现(以一种故意缓慢的方式)

这是意料之中的——Ruby使用绿色线程,这意味着单个Ruby进程一次不能消耗超过1个CPU核。我的机器有两个内核,所以在分叉时运行速度大约是原来的两倍(允许分叉开销)

如果我在JRuby下运行它,它确实有本机线程(即,实际的进程内并发),我会得到如下结果:

$ ruby fib.rb
                 user       system     total    real
Single threaded  4.050000   0.000000   4.050000 (  4.054188)
Multithreaded    4.100000   0.000000   4.100000 (  4.114595)
Forked           0.000000   0.000000   4.000000 (  2.054361)
$ ruby fib.rb
                user        system    total     real
Single threaded 27.850000   0.100000  27.950000 ( 27.812978)
Multithreaded   27.870000   0.060000  27.930000 ( 14.355506)
进程内线程确实将任务的运行时间减半(不过,yikes,这似乎是JRuby特别不擅长的一个)


您可能会想,如果Ruby不能在每个进程中使用多个内核,为什么它会提供线程——这是因为在等待IO时,您仍然可以跨线程工作!如果您的应用程序花费大量时间与网络套接字通信(例如进行数据库查询),那么您将看到多线程处理带来的并发性收益,即在阻塞套接字时让其他线程工作。

分叉不是线程处理,你所做的一切就是把一个进程分叉,然后休眠它——你实际上没有做任何计算工作。您在500次迭代测试中观察到的可能是分叉新流程的开销。你到底想测试什么?@ChrisHeald我应该修改我的脚本以使用线程,然后运行try测试吗?这取决于你实际要测试的内容。到目前为止还不清楚-您必须做一些工作来测试并行性。您可以休眠尽可能多的进程或线程。@ChrisHeald我试图通过在两种情况下执行相同的操作来演示多线程与单线程的一些好处。除此之外,MRI不使用多核。您需要使用JRuby或Rubinius来利用真正的并行性。分叉不是线程,您所做的只是分叉一个进程,然后将其休眠—您实际上没有做任何计算工作。您在500次迭代测试中观察到的可能是分叉新流程的开销。你到底想测试什么?@ChrisHeald我应该修改我的脚本以使用线程,然后运行try测试吗?这取决于你实际要测试的内容。到目前为止还不清楚-您必须做一些工作来测试并行性。您可以休眠尽可能多的进程或线程。@ChrisHeald我试图通过在两种情况下执行相同的操作来演示多线程与单线程的一些好处。除此之外,MRI不使用多线程