是什么让Ruby变慢?

时间:2009-06-18 08:28:35

标签: ruby performance garbage-collection dynamic-languages

Ruby在某些方面很慢。但是哪个部分最成问题呢?

垃圾收集器对性能有多大影响?我知道有时候单独运行垃圾收集器需要几秒钟,特别是在使用OpenGL库时。

我使用Ruby的矩阵数学库特别慢。 ruby如何实现基本数学有问题吗?

Ruby中是否有任何动态功能无法有效实现?如果是这样,Lua和Python等其他语言如何解决这些问题?

最近的工作是否有显着改善的表现?

9 个答案:

答案 0 :(得分:69)

  

Ruby很慢。但是哪个部分最成问题呢?

它对方法进行“延迟查找”,以实现灵活性。这会减慢它的速度。它还必须记住每个上下文的变量名称以允许eval,因此它的帧和方法调用较慢。它目前缺少一个好的JIT编译器,虽然MRI 1.9有一个字节码编译器(更好),jruby将其编译成java字节码,然后(可以)通过HotSpot JVM的JIT编译器进行编译,但它最终是关于速度与1.9相同。

  

垃圾收集器对性能有多大影响?我知道有时候单独运行垃圾收集器需要几秒钟,特别是在使用OpenGL库时。

来自http://www.igvita.com/2009/06/13/profiling-ruby-with-googles-perftools/的一些图表我认为大概需要10% - 这可以通过增加gc.c中的malloc_limit并重新编译来减少这一点。

  

我使用Ruby的矩阵数学库特别慢。 ruby如何实现基本数学有问题吗?

Ruby 1.8“没有”实现基本数学它实现了数字类,你会调用像Fixnum#+ Fixnum#/每次调用一次 - 这很慢。 Ruby 1.9通过内联一些基本的数学操作来作弊。

  

Ruby中是否有任何动态功能无法有效实现?如果是这样,Lua和Python等其他语言如何解决这些问题?

像eval这样的东西很难有效地实施,虽然可以做很多工作,我敢肯定。 Ruby的踢球者是它必须容纳某人在另一个线程中自发地改变一个类的定义,所以它必须非常保守。

  

最近的工作是否有显着改善的表现?

1.9就像2倍的加速。它也更节省空间。 JRuby不断尝试提高速度[并且可能在GC中花费的时间少于KRI]。除此之外,除了我一直在努力的小事情之外,我并没有意识到这一点。另请注意,由于编码友好性,1.9的字符串有时会变慢。

答案 1 :(得分:11)

Ruby非常适合快速提供解决方案。提供快速解决方案的效果较差。这取决于你想要解决的问题。我想起了90年代早期关于旧CompuServe MSBASIC论坛的讨论:当被问及哪个更快的Windows开发,VB或C时,通常的答案是“VB,大约6个月”。

在MRI 1.8形式中,Ruby执行某些类型的计算密集型任务相对较慢。与大多数主流编译语言相比,几乎任何解释性语言都会受到影响。

原因有几个:一些相当容易解决(例如1.8中的原始垃圾收集),有些则不那么容易。

1.9解决了一些问题,尽管它可能需要一段时间才能普遍使用。针对预先存在的运行时的一些其他实现,例如JRuby,IronRuby,MagLev,有可能明显更快。

关于数学性能,看到相当慢的吞吐量我不会感到惊讶:它是你为任意精度支付的价格的一部分。再次,选择你的问题。我已经解决了Ruby中70多个Project Euler问题,几乎没有解决方案需要花费超过一分钟才能运行。你需要多快才能运行它,你需要多久才能运行它?

答案 2 :(得分:9)

最有问题的部分是“每个人”。

如果“每个人”都没有真正使用该语言,那么奖励积分。

说真的,1.9速度更快,现在与python相当,jruby比jython快。

垃圾收集器随处可见;例如,Java有一个,它在动态内存处理方面比C ++更快。 Ruby不适合数字运算;但是很少有语言,所以如果你的程序中有任何语言的计算密集型部分,你最好用C语言重写它们(由于它的原始类型,Java在数学方面很快,但它为它们付出了很大的代价,它们显然是#语言中最丑陋的部分1。)

至于动态功能:它们并不快,但静态语言中没有它们的代码甚至可能更慢;例如,java将使用XML配置而不是使用DSL的Ruby;因为XML解析成本很高,所以可能会很慢。

答案 3 :(得分:8)

嗯 - 几年前我参与了一个项目,在那里我用Ruby性能刮掉了桶,我不确定自那以后有多少变化。现在它是一个警告 - 你必须知道不做某些事情,坦率地说游戏/实时应用程序将是其中之一(因为你提到OpenGL)。

杀死交互性能的罪魁祸首是垃圾收集器 - 其他人在这里提到Java和其他环境也有垃圾收集,但Ruby必须阻止世界运行。也就是说,它必须停止运行程序,从头开始扫描每个寄存器和内存指针,标记仍在使用的内存,然后释放剩下的内存。发生这种情况时,不能中断该过程,正如您可能已经注意到的那样,可能需要数百毫秒。

它的执行频率和长度与您创建和销毁的对象数量成正比,但除非您完全禁用它,否则无法控制。我的经验是有几个令人不满意的策略来平滑我的Ruby动画循环:

  • GC.disable / GC.enable围绕关键的动画循环,也许是一个机会主义的GC.start,当它不会造成任何伤害时强制它去。 (因为当时我的目标平台是64MB的Windows NT机器,这导致系统偶尔耗尽内存。但从根本上说这是一个坏主意 - 除非你可以预先计算出你可能需要多少内存, “冒着记忆力耗尽的风险”
  • 减少您创建的对象数量,以便GC减少工作量(减少执行的频率/长度)
  • 用C语言重写你的动画循环(一个警察出局,但我去的那个!)

这些天我可能还会看到JRuby是否可以作为替代运行时,因为我相信它依赖于Java更复杂的垃圾收集器。

我发现的另一个主要性能问题是尝试在Ruby中编写TFTP服务器时的基本I / O(是的,我为我的性能关键项目选择了所有最好的语言,这只是一个实验) 。简单地响应一个UDP数据包与另一个UDP数据包的绝对最简单的循环,包含下一个文件,必须比库存C版本慢大约20倍。我怀疑在使用低级IO(sysread等)的基础上可能会有一些改进,但速度可能只是因为没有低级字节数据类型 - 每个小读取都被复制到一个串。这只是猜测,我没有进一步采用这个项目,但它警告我不要依赖于快速的I / O.

最近增长的主要速度虽然我在这里并不完全是最新的,但虚拟机实现重做了1.9,从而加快了代码执行速度。但是I don't think the GC has changed,我很确定I / O方面没什么新东西。但是我并没有完全了解最前沿的Ruby,所以其他人可能想要在这里进行切换。

答案 4 :(得分:4)

Steve Dekorte:“用高级语言编写Mandelbrot集计算器就像试图在公交车上运行Indy 500一样。”

http://www.dekorte.com/blog/blog.cgi?do=item&id=4047

我建议学习各种工具,以便使用合适的工具。使用高级API可以有效地完成矩阵变换,该API使用算术密集型计算来包装紧密循环。有关将C或C ++代码嵌入Ruby脚本的示例,请参阅RubyInline gem。

还有一种比Ruby慢得多的Io语言,但它通过使用SIMD加速技术在Pixar中有效地渲染电影并且在矢量算术上优于原始C语言。

答案 5 :(得分:4)

我认为你在问,“Ruby中的哪些特殊技术往往很慢。”

一个是对象实例化。如果您正在进行大量操作,那么您希望查看(合理的)减少方法,例如使用flyweight pattern,即使内存使用不是问题。在我重新编写的一个库中,它不是一遍又一遍地创建很多非常相似的对象,我将库的整体速度提高了一倍。

答案 6 :(得分:3)

根据一些基准测试,Ruby 1.9.1的速度是PHP的两倍,比Perl快一点。

(更新:我的来源是thisscreenshot)。但我不知道他的来源是什么。)

Ruby并不慢。旧的1.8是,但目前的Ruby不是。

答案 7 :(得分:2)

Ruby很慢,因为它旨在优化程序员的体验,而不是程序的执行时间。缓慢只是设计决策的一个症状。如果您更喜欢表演,那么您应该使用不同的语言。 Ruby不是一切。

答案 8 :(得分:1)

IMO,动态语言一般都很慢。他们在运行时做一些静态语言在编译时做的事情。

语法检查,解释和类似检查,转换。这是不可避免的,因此ruby比c / c ++ / java慢,如果我错了,请纠正我。