`
jack
  • 浏览: 392823 次
  • 来自: 温州
社区版块
存档分类
最新评论

ruby线程运行速度测试

阅读更多
首先声明下,纯粹是想看下ruby thread和native thread的差距,并无意贬低ruby.

测试结果吓一跳,不是太快。和native thread相比,实在太慢。

代码很简单,每次启动10个ruby thread,做100000次的累加。连续运行10次,求运行平均值。
我的工作机,PC 2.8G,单核。1G内存,windows XP。测试的时候也没有特别清理运行环境,
最后的平均结果3.0937

time = 0
10.times do |k|
now = Time.now
puts now.to_s

count = 0
threads = []
10.times do |i|
	threads[i] = Thread.new do
		#sleep(rand(0.1))
		
		100000.times do |j|
			count += j
		end
		Thread.current["mycount"] = count
	end
end
	
	threads.each {|t| t.join; print t["mycount"], ", " }
	puts "count = #{count}"

later = Time.now
time += later-now
puts later-now
end
puts time/10


然后同样的代码用C++写了一份,运行时间都测试不出来,时间差值几乎是0。几乎程序刚刚启动,运行就结束了。不像Ruby代码,还像模像样的运行了段时间。

HOHO。


分享到:
评论
29 楼 歆渊 2006-11-05  
charon 写道
whisper 写道

强迫线程的原子操作会巨大的降低系统整体性能
毕竟同步可不是个常见的工作

以机器码执行或做过机器码优化的可能有这个问题。但是对使用GIL的解释执行环境下处理这个原子操作应该非常容易,实现原子操作并不需要额外的同步。

Nod, 只要选择在原子操作的边界来切换green thread上下文就可以了.
28 楼 歆渊 2006-11-05  
aardvark 写道

有。obj.synchronize

require 'thread'
mutex = Mutex.new
cv = ConditionVariable.new

a = Thread.new {
  mutex.synchronize {
    puts "A: I have critical section, but will wait for cv"
    cv.wait(mutex)
    puts "A: I have critical section again! I rule!"
  }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
  mutex.synchronize {
    puts "B: Now I am critical, but am done with cv"
    cv.signal
    puts "B: I am still critical, finishing up"
  }
}
a.join
b.join


不错不错, 用法和Java的一样精妙, 可以有效防止复杂代码块里忘记解锁而产生bug的倾向.
而且看样子是通过库扩展来实现的, 有了closure真的方便很多, Java还死抱着Anonymous Class来应付这种要求确实有点不尽人意.
27 楼 charon 2006-11-05  
whisper 写道

强迫线程的原子操作会巨大的降低系统整体性能
毕竟同步可不是个常见的工作

以机器码执行或做过机器码优化的可能有这个问题。但是对使用GIL的解释执行环境下处理这个原子操作应该非常容易,实现原子操作并不需要额外的同步。
26 楼 whisper 2006-11-05  
charon 写道
complystill 写道
charon 写道
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7


+= 在Java里也不是原子操作的, 需要对外部资源进行显式的synchronization.

不过既然是伪线程, 解释器去负责调度的, 按说应该比较容易实现成原子操作的.

不知道Ruby里有没有类似Java的 synchronized(obj) { ... }  这种机制?


像java那样的并发native thread,除非硬件/操作系统对+=实现了原子操作,否则靠VM自己是做不到原子操作这个份上。
像python那样的GIL来串行化线程的机制,有能力做到+=原子操作,而且看起来是做到了+=的原子操作。至少与当前ruby程序相应的python程序,总结果每次都是一样的。
而像ruby这样的green thread方式,应当更加容易做到啊。毕竟,做到这一点之后,对线程编程模型有很大的便利.

强迫线程的原子操作会巨大的降低系统整体性能
毕竟同步可不是个常见的工作
似乎OpenMP可以自动的同步
这个我并不太了解
25 楼 jack 2006-11-05  
aotianlong 写道
废话!!!
你用别的脚本语言比如PERL,PYTHON等来运行这样的计算,也需要那么长时间。

脚本怎么能跟C++相比,即使是JAVA也比不上C++更何况脚本了。


再说RUBY还不是用C写出来的?
哎,不再同一个运行级别上的东西。


不是同一个运行级别上的代码就不能做比较吗? 你这是什么意思?
知道这种差距不是很容易根据项目需要选择不同的语言来开发吗?
24 楼 aotianlong 2006-11-04  
废话!!!
你用别的脚本语言比如PERL,PYTHON等来运行这样的计算,也需要那么长时间。

脚本怎么能跟C++相比,即使是JAVA也比不上C++更何况脚本了。


再说RUBY还不是用C写出来的?
哎,不再同一个运行级别上的东西。
23 楼 charon 2006-11-04  
complystill 写道
charon 写道
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7


+= 在Java里也不是原子操作的, 需要对外部资源进行显式的synchronization.

不过既然是伪线程, 解释器去负责调度的, 按说应该比较容易实现成原子操作的.

不知道Ruby里有没有类似Java的 synchronized(obj) { ... }  这种机制?


像java那样的并发native thread,除非硬件/操作系统对+=实现了原子操作,否则靠VM自己是做不到原子操作这个份上。
像python那样的GIL来串行化线程的机制,有能力做到+=原子操作,而且看起来是做到了+=的原子操作。至少与当前ruby程序相应的python程序,总结果每次都是一样的。
而像ruby这样的green thread方式,应当更加容易做到啊。毕竟,做到这一点之后,对线程编程模型有很大的便利.
22 楼 aardvark 2006-11-03  
complystill 写道
charon 写道
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7


+= 在Java里也不是原子操作的, 需要对外部资源进行显式的synchronization.

不过既然是伪线程, 解释器去负责调度的, 按说应该比较容易实现成原子操作的.

不知道Ruby里有没有类似Java的 synchronized(obj) { ... }  这种机制?

有。obj.synchronize

require 'thread'
mutex = Mutex.new
cv = ConditionVariable.new

a = Thread.new {
  mutex.synchronize {
    puts "A: I have critical section, but will wait for cv"
    cv.wait(mutex)
    puts "A: I have critical section again! I rule!"
  }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
  mutex.synchronize {
    puts "B: Now I am critical, but am done with cv"
    cv.signal
    puts "B: I am still critical, finishing up"
  }
}
a.join
b.join

21 楼 whisper 2006-11-03  
P4 2.8 HT
1G RAM
Ubuntu 6.06
Linux 2.6.15_27 SMP
Ruby 1.8.3
难道是我RP高?
20 楼 whisper 2006-11-03  
P4 2.8 HT
1G RAM
Ubuntu 6.06
Linux 2.6.15_27 SMP
Ruby 1.8.3
难道是我RP高?
19 楼 jack 2006-11-03  
whisper 写道
哪位解释一下为什么我的输出是0.16
我机器又没这么强悍 :-P

那段ruby代码的结果是0.16?夸张了点。你的环境是什么样子的?
18 楼 whisper 2006-11-03  
哪位解释一下为什么我的输出是0.16
我机器又没这么强悍 :-P
17 楼 歆渊 2006-11-03  
charon 写道
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7


+= 在Java里也不是原子操作的, 需要对外部资源进行显式的synchronization.

不过既然是伪线程, 解释器去负责调度的, 按说应该比较容易实现成原子操作的.

不知道Ruby里有没有类似Java的 synchronized(obj) { ... }  这种机制?
16 楼 abcd123efg123 2006-11-03  
应该不会溢出吧?

15 楼 jack 2006-11-03  
charon 写道
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7


对哦,我的输出结果,这个的确奇怪.

Fri Nov 03 09:40:15 -0800 2006
count = 6639792732
3.547
Fri Nov 03 09:40:18 -0800 2006
count = 6713860038
3.828
Fri Nov 03 09:40:22 -0800 2006
count = 6474941112
3.359
Fri Nov 03 09:40:26 -0800 2006
count = 6540511590
2.766
Fri Nov 03 09:40:28 -0800 2006
count = 6543815896
2.734
Fri Nov 03 09:40:31 -0800 2006
count = 6660264966
2.766
Fri Nov 03 09:40:34 -0800 2006
count = 6553154092
2.922
Fri Nov 03 09:40:37 -0800 2006
count = 6630718756
2.765
Fri Nov 03 09:40:40 -0800 2006
count = 6582592512
2.797
Fri Nov 03 09:40:42 -0800 2006
count = 6523668315
2.781
30.265

如果只运行一次,每次10个线程累加 结果都是count = 6639792732

估计是溢出了,这里是10个线程都累加到一个count上,如果每一个thread都有自己的count,累加下来就是49999500000没错的.
14 楼 jack 2006-11-03  
aardvark 写道
这个测试远远远远远远偏离了初衷...

C++是编译到直接在CPU上跑的机器码的,Ruby是需要一边解释一边执行的。这个测试看到的性能差异是这个不同导致的性能差异。

从理论上说,单(核)CPU上伪线程和native线程在性能上的差异应该是很小的,甚至伪线程的性能可能更高(依赖于实现)。伪线程在性能上的劣势只有在多核多CPU上才会显现出来。

要想比较伪线程和native线程的性能差异,可以这样做:

1)把程序改成单线程,可能需要加大C++的运算量,测出Ruby vs. C++的性能。假设这个性能比是1:n。

2)把程序改回多线程,测试Ruby vs. C++的性能。假设这个性能比是1:m。

3)比较m和n



按你这个方法测试了下,改成10000000次累加,另外ruby运行速度实在太慢,不在循环测试10次求平均
单位是毫秒

第一项是 45645:32
第二项是 439000:469
n:m的结果 0.656



13 楼 charon 2006-11-03  
又一个非常小的疑问。
为什么每次执行,最后打印的那个count都是不一样的。
按理说,如果都是串行排队的,都应该是49999500000
难道ruby的green线程调度中,+=不是原子操作?
我用python写了一个类似的脚本,每次执行的最终结果都是一样的(当然每个线程的count结果每次都不一样),耗时大概是ruby脚本的1/7
12 楼 aardvark 2006-11-03  
robbin 写道
ruby的线程都不能说是伪线程,应该说假线程,你跑10个线程其实就是10个任务在串行排队执行。

伪线程和任务都是术语,不好乱说的,容易让人误会。
而且,ruby的10个线程也不是“串行排队”,和native线程一样是分时的。
11 楼 aardvark 2006-11-03  
这个测试远远远远远远偏离了初衷...

C++是编译到直接在CPU上跑的机器码的,Ruby是需要一边解释一边执行的。这个测试看到的性能差异是这个不同导致的性能差异。

从理论上说,单(核)CPU上伪线程和native线程在性能上的差异应该是很小的,甚至伪线程的性能可能更高(依赖于实现)。伪线程在性能上的劣势只有在多核多CPU上才会显现出来。

要想比较伪线程和native线程的性能差异,可以这样做:

1)把程序改成单线程,可能需要加大C++的运算量,测出Ruby vs. C++的性能。假设这个性能比是1:n。

2)把程序改回多线程,测试Ruby vs. C++的性能。假设这个性能比是1:m。

3)比较m和n
10 楼 robbin 2006-11-02  
ruby的线程都不能说是伪线程,应该说假线程,你跑10个线程其实就是10个任务在串行排队执行。

相关推荐

    Ruby多线程编程初步入门

    这意味着即使在多核处理器上,Ruby线程也不能实现真正的并行执行。然而,通过合理的设计和使用外部库(如多进程、Fiber等),仍然可以在Ruby中实现高性能的并发程序。 #### 四、示例代码分析 以下是一个简单的Ruby...

    Working with Ruby Threads

    ### Ruby线程开发技术详解 #### 引言 在计算机科学领域中,多线程编程是一种常见的技术,它允许多个任务同时运行在一个程序中。《Working with Ruby Threads》这本书深入探讨了Ruby语言中的线程机制,对于希望利用...

    多线程和并行程序设计

    我们创建了三个线程,每个线程运行不同的任务。这些线程在执行过程中,会在控制台上轮流打印字母和数字。这种设计使得任务的执行并行化,从用户的角度来看,输出的内容好像是线程同时运行产生的。 然而,在并发编程...

    Ruby-rubyinstall安装RubyJRubyRubiniusMagLevorMRuby

    通过这个工具,开发者可以轻松地在本地系统上设置多个Ruby版本,无需担心它们之间的冲突,这对于测试、开发和维护工作来说非常有用。 首先,让我们详细了解下这些不同的Ruby实现: 1. MRI (Matz's Ruby ...

    ruby-2.6.3源码压缩包

    4. **改进的并发模型**:Ruby 2.6加强了线程和并发处理,如更好的线程局部变量支持,以及线程池的优化,提升了多线程环境下的程序性能。 5. **新的垃圾回收(Garbage Collection, GC)算法**:引入了Shenandoah垃圾...

    ruby下载太慢保存下

    5. **使用带宽优化工具**:如`aria2`,这是一个支持多源、多线程下载的工具,可以提升大文件的下载速度。 描述中的“ruby下载太慢,上传一个”可能意味着提供者已经将Ruby的特定版本(如2.6.6)下载并打包,供他人...

    Ruby-ParallelRuby让并行处理简单和快速

    无论你是进行大数据分析、批量任务处理还是其他计算密集型工作,Parallel Ruby都能帮助你充分利用系统资源,提高代码的运行速度。通过熟练掌握这个库,你将能够编写出更加高效、响应更快的Ruby应用程序。

    ruby-enterprise-1.8.6-20090201

    1. **Performance Enhancements(性能提升)**:通过引入了一个名为“Maglev”的Garbage Collector(垃圾收集器),REE显著提高了Ruby应用程序的运行速度和内存效率。Maglev GC是一个全托管的数据库风格的GC,它可以...

    ruby2.6.1.zip

    3. **线程并行性增强**:Ruby 2.6加强了线程管理,允许在多核处理器上实现更好的并发性能,通过改进GVL(全局解释器锁)的实现,减少了线程之间的同步开销。 4. **安全的数组和哈希迭代**:为了防止某些类型的安全...

    ruby 1.9.3

    在性能方面,Ruby 1.9.3通过改进垃圾回收机制和提升解释器效率,显著提升了运行速度。这意味着开发者可以编写更复杂的程序,而不必过于担心性能问题。同时,1.9.3还引入了线程局部变量,为多线程编程提供了更好的...

    Ruby v2.6.3 正式版

    10. **YARV虚拟机**:Ruby 2.6.3使用YARV(Yet Another Ruby VM)作为其默认的解释器,这是一个高效的字节码虚拟机,提高了Ruby的执行速度。 在压缩包中,"说明.htm"可能是关于Ruby 2.6.3安装、配置或使用的指南,...

    ruby-2.7.0.tar.gz

    5. **性能提升**: Ruby 2.7对垃圾回收机制进行了优化,提升了整体运行速度,尤其是对于循环引用的处理。 6. **模块合并的改进**: 对于模块的合并操作,现在支持更灵活的控制,允许保留或覆盖已有方法,提高了代码...

    Ruby-forkoffruby简单的并行处理

    Ruby是一种动态、面向...通过合理利用`ForkOff`,开发者可以优化代码,提升程序的运行速度,尤其在处理大量数据或执行多个独立任务时效果显著。不过,使用时需注意其与线程的区别,以及进程间通信和资源管理的问题。

    ruby and it's tp

    线程池则是一种多线程处理形式,预先创建一组线程,用于执行各种任务,以提高系统的效率和响应速度。 在Ruby中,线程是通过Thread类来实现的。你可以创建一个新的线程并启动它,以执行特定的代码块。然而,直接创建...

    ruby-2.7.1.zip

    这个版本引入了多项性能提升措施,使得代码执行速度更快,尤其在处理大量数据或运行复杂算法时。此外,它还改善了内存管理,减少了内存占用,这对于资源有限的环境或大型应用来说非常关键。 另一个显著的变化是引入...

    ruby on rails 常用插件下载

    在 Ruby on Rails 中,多线程是一种处理并发请求的方式,尤其在现代硬件支持多核处理器的情况下,合理利用多线程可以有效地提升应用的响应速度。Rails 默认并不完全支持多线程,因为 Ruby 的 GIL(全局解释器锁)会...

    Ruby-Wombat轻量级的RubyWeb爬虫

    - **性能优化**:Wombat支持多线程和并发抓取,以提高爬取速度。但过度并发可能导致服务器压力过大,应适当调整并发级别,确保不影响网站正常运行。 - **数据清洗**:提取的数据可能包含噪声或无效信息,因此在处理...

Global site tag (gtag.js) - Google Analytics