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

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个任务在串行排队执行。

相关推荐

    MYSQL 5.7.12.zip

    MySQL (数据库服务器)是一款小型关联式数据库管理系统,MySQL (数据库服务器)的执行性能非常高,运行速度非常快,并非常容易使用,是一个非常捧的数据库。MySQL (数据库服务器)在世界上是最流行的数据库语言。...

    Linux mysql-5.7.12

    MySQL (数据库服务器)是一款小型关联式数据库管理系统,MySQL (数据库服务器)的执行性能非常高,运行速度非常快,并非常容易使用,是一个非常捧的数据库。MySQL (数据库服务器)在世界上是最流行的数据库语言。...

    puma:为并发性而构建的RubyRack Web服务器

    在MRI上,有一个全局VM锁(GVL),可确保一次只有一个线程可以运行Ruby代码。 但是,如果您要进行大量的阻塞IO(例如对外部API(如Twitter)的HTTP调用),Puma仍然可以通过允许IO等待并行进行来提高MRI的吞吐量。 ...

    threaded:比演员轻,然后螺纹更简单。 穿线!

    如果您正在MRI中执行IO,或者在JRuby中执行任何操作,则可以使用线程来显着加快程序速度。 但是,线程是Ruby中的低级原语,可能很难使用。 Threaded库将一些常见的线程模式实现到易于使用的界面中。 这使您可以专注...

    async_horror:使用异步进行实验

    但是,这并不一定意味着它们将同时运行(例如,单个线程上有多个线程)内核机器。相反,并行是指两个任务在字面上同时运行(例如,多核处理器上的多个线程)” 为什么大量线程的性能很差? “答案是许多Ruby程序员...

    rust-python-example:使用Rust扩展Python的示例

    Rust是一种运行速度极快,防止出现段错误并保证线程安全的系统编程语言。 特色 零成本抽象 移动语义 保证内存安全 没有数据争用的线程 基于特征的泛型 模式匹配 类型推断 最少的运行时间 有效的C绑定 摘自:来自...

    delayed_job_celluloid

    通过在单个多线程进程中运行工作线程,delayed_job_celluloid 在内存使用和速度方面更加高效。安装将 delay_job_celluloid 添加到您的 gem 文件中 gem 'delayed_job_celluloid'运行捆绑安装 bundle install delayed...

    精通Qt4编程(第二版)源代码

    另外,还有一些开源的在其他语言上的Qt绑定,如C#/Mono的绑定Qyoto,Python的绑定PyQt,Ruby的绑定QtRuby等。有了这些产品,编写Qt程序不再是C++程序员的专利了。 \Qt的发行版本有商业版和开源版。开源版遵循QPL(Q...

    精通qt4编程(源代码)

    \ 第11章 事件机制 李立夏介绍了Qt的事件处理模型,详细介绍了在Qt程序设计中处理事件的五种方法,并讨论了如何利用Qt事件机制加快用户界面响应速度。 283 \ 第12章 数据库 李立夏介绍了Qt的数据库处理,重点介绍了...

    国外java源码社区-Awesome-IQDevs:IQDevs社区收集的工具和资源的集合

    是一种系统编程语言,运行速度极快,可以防止段错误,并保证线程安全 :有史以来使用最广泛的编程语言之一 : 具有命令式、面向对象和通用编程功能,同时还提供用于低级内存操作的设施 移动开发 : 一个用 React 构建...

    em-files:通过EventMachine排序文件读取器和写入器。 解决在处理大文件时阻塞磁盘IO的问题

    它们肯定会加快文件IO操作的速度,但允许同时运行其他任务(从EM的角度来看)。 毫无疑问,这是否有意义,因为EM::defer可用于处理这些阻止任务。 但有时是不希望在单独的线程中执行它们的情况。 API与类代表的经典...

    nixon-pi:用覆盆子pi和生物识别盾牌控制ogilumen数码管

    它由一个多线程的ruby应用程序驱动,该应用程序由I / O驱动程序,状态机,基于sinatra的Web应用程序和通过消息队列连接的json api组成。 最初是一个私人想法,后来发现它已成为在LAB团队活动中的一个小型项目。 ...

    教务系统设计数据库设计.doc

    MySQL数据库具有以下特性: 使用C和C++编写,并使用了多种编译器进行测试,保证源代码的可移植性。 支持AIX、FreeBSD、HP-UX、Linux、Mac OS、Novell Netware、OpenBSD、OS/2 Wrap、Solaris、Windows等多种操作系统...

    教务系统设计数据库设计(1).doc

    MySQL数据库具有以下特性: 使用C和C++编写,并使用了多种编译器进行测试,保证源代码的可移植性。 支持AIX、FreeBSD、HP-UX、Linux、Mac OS、Novell Netware、OpenBSD、OS/2 Wrap、Solaris、Windows等多种操作系统...

    从入门到精通HTML5——PDF——网盘链接

     10.1.4 滚动速度属性——scrollamount 205  10.1.5 滚动延迟属性——scrolldelay 206  10.1.6 滚动循环属性——loop 207  10.1.7 滚动范围属性——width、height 208  10.1.8 滚动背景颜色属性——bgcolor 209...

Global site tag (gtag.js) - Google Analytics