【问题标题】:What makes Ruby slow? [closed]是什么让 Ruby 变慢了? [关闭]
【发布时间】:2010-11-03 22:20:54
【问题描述】:

Ruby 在某些事情上很慢。但它的哪些部分最有问题?

垃圾收集器对性能有多大影响?我知道我曾经有过单独运行垃圾收集器需要几秒钟的时间,尤其是在使用 OpenGL 库时。

我在 Ruby 中使用过特别慢的矩阵数学库。 ruby 实现基本数学的方式有问题吗?

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

最近有没有显着提高性能的工作?

【问题讨论】:

  • 我做了一些小改动,以缩减您 Q 的争论方面。希望对您有用。
  • 稍微偏离主题:如果您想使用 Ruby 但又对其性能感到困扰,那么明智的做法是使用 C 扩展对性能关键部分进行编码。当然,使用 C 语言你甚至可以编写汇编代码,因此这些部分很容易让纯 Java 实现成为可能。

标签: ruby performance garbage-collection dynamic-languages


【解决方案1】:

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.9 在基本数学方面有所改进,这很好——我现在必须尝试一下。我希望 ruby​​ 程序员不要这么多地使用 eval,但有时我偶然发现一个带有插值的半字符串文字类。这似乎是错误的。
【解决方案2】:

Ruby 非常适合快速交付解决方案。提供快速解决方案的情况较少。这取决于您要解决什么样的问题。我想起了 90 年代初在旧 CompuServe MSBASIC 论坛上的讨论:当被问及 Windows 开发、VB 还是 C 哪个更快时,通常的答案是“VB,大约 6 个月”。

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

原因有很多:一些相当容易处理(例如 1.8 中的原始垃圾收集),一些则不太容易。

1.9 解决了一些问题,尽管它可能需要一段时间才能普遍可用。其他一些以预先存在的运行时为目标的实现,例如 JRuby、IronRuby、MagLev,有可能显着加快速度。

关于数学性能,如果吞吐量相当缓慢,我不会感到惊讶:这是您为任意精度付出的代价的一部分。再次,选择你的问题。我已经解决了 70 多个 Ruby 中的 Project Euler 问题,几乎没有解决方案需要超过一分钟的时间来运行。您需要它以多快的速度运行以及需要多长时间?

【讨论】:

  • 我同意。如果性能是一个问题,则说明您使用了错误的工具来完成这项工作。
【解决方案3】:

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

如果“每个人”都没有真正使用过该语言,则可以加分。

说真的,1.9 快得多,现在与 python 相当,jruby 比 jython 快。

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

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

【讨论】:

  • 不不,我喜欢垃圾收集器。我只是想知道是否可以改进 ruby​​ 的垃圾收集器实现。通过动态特性,我想知道是否有什么东西让 ruby​​ 比 Python 或 Lua 更难优化。 1.9 破坏了与 1.8 的兼容性——现在有很多库支持吗?
  • 另外,关于“每个人”的那一点完全不是我的意思。我已将其从问题中删除。我用过很多红宝石。看起来,尽管有效地实现了一些东西(例如符号),但还有很多速度较慢的东西(比如注入)。
  • 好吧,你的代码慢吗?如果是,您是否对其进行了简介?一切都可以改进,但 jruby 比 jython 更快(与 python 和其他基于 C 的语言相当)但与 1.8 兼容。
  • python 不是基于 C 的语言——不过,它的实现是,如果这就是你的意思的话。
  • 我的座右铭是:“快速交付,稍后优化” :-) 对于数字运算,我在 Pascal 中“原型化”并使用内联汇编对其进行重构。至于 Ruby 和 Mandelbrot 示例:您在说什么“慢代码”?在 16000x16000 画布上绘制 Mandelbrot 集的 C 实现需要大约 30 秒才能完成,如果你只是在 Ruby 1.9.2 中创建两个嵌套的 1..16000 的 FOR 循环,它什么都不做,它将需要 40- 50秒完成。 empty FOR 循环自己!将 FOR 更改为 WHILE,并将 "+=" 替换为 .succ 将其缩短到大约 30 秒。
【解决方案4】:

嗯 - 几年前我参与了一个项目,我在 Ruby 性能方面遇到了困难,但我不确定从那以后发生了多大的变化。现在它是警告购买者 - 你必须知道不要做某些事情,坦率地说游戏/实时应用程序将是其中之一(因为你提到了 OpenGL)。

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

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

  • GC.disable / GC.enable 围绕关键动画循环,可能是一个机会主义的 GC.start 在它不会造成任何伤害时强制它去。 (因为我当时的目标平台是一台 64MB 的 Windows NT 机器,这会导致系统偶尔耗尽内存。但从根本上说,这是一个坏主意 - 除非您可以在执行此操作之前预先计算您可能需要多少内存,否则您'冒着内存耗尽的风险)
  • 减少您创建的对象数量,从而减少 GC 要做的工作(减少其执行的频率/长度)
  • 用 C 重写你的动画循环(一个逃避,但我选择了那个!)

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

我发现的另一个主要性能问题是不久前尝试在 Ruby 中编写 TFTP 服务器时的基本 I/O(是的,我为我的性能关键项目选择了所有最好的语言,这只是一个实验) .最简单的最紧凑的循环简单地响应一个 UDP 数据包与另一个,包含文件的下一部分,肯定比股票 C 版本慢 20 倍。我怀疑基于使用低级 IO(sysread 等)可能已经进行了一些改进,但速度缓慢可能只是因为没有低级字节数据类型 - 每个小读取都被复制到一个细绳。虽然这只是猜测,我并没有进一步推进这个项目,但它警告我不要依赖 snappy I/O。

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

【讨论】:

  • 很好的答案!如果我可以选择两个答案,我也会选择你。是的,尝试在 Ruby 中构建实时图形应用程序可能很愚蠢,我最终禁用并手动触发了帧之间的 GC,以使其动画至少有点平滑,如果不是很慢的话。我在 Python 上的运气更好——尤其是 Pyglet,这是一个了不起的小型 opengl 库,它比 SDL 有用得多,现在似乎每个人都在使用它。
【解决方案5】:

我假设您在问,“Ruby 中的哪些特定技术往往很慢。”

一个是对象实例化。如果你正在做大量的事情,你想看看(合理的)减少它的方法,比如使用flyweight pattern,即使内存使用不是问题。在一个库中,我对其进行了重新设计,不再一遍又一遍地创建大量非常相似的对象,我将库的整体速度提高了一倍。

【讨论】:

    【解决方案6】:

    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 语言,但它在 Pixar 中有效地渲染电影,并通过使用 SIMD 加速在矢量算术上优于原始 C。

    【讨论】:

      【解决方案7】:

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

      (更新:我的来源是this (screenshot)。不过我不知道他的来源是什么。)

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

      【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

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

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

        【讨论】:

          猜你喜欢
          • 2020-04-09
          • 2016-01-13
          • 2011-05-21
          • 2011-04-26
          • 2022-01-08
          • 1970-01-01
          • 1970-01-01
          • 2023-04-08
          • 2011-09-23
          相关资源
          最近更新 更多