Ruby Ruby 和 Java 的性能差距太大了

tomanderson · 2021年01月26日 · 最后由 ericguo 回复于 2021年03月05日 · 7021 次阅读

都知道 ruby 的性能是弱项,但心里总觉得 ruby 一直在进步,差距会越来越小的。

今天为了试验 ractor,写了一个非常简单的斐波那契数列求和:

def fib(n)
  return n if n == 0 || n == 1
  fib(n-1) + fib(n-2)
end

一台小破笔记本,ruby2.7,单线程跑 fib(40),14 秒。开 jit 后 5 秒(之前以为开不开 jit 一样,后来发现是 rails 的问题,代码是放在 rails 里跑的)。

当时没觉得有啥,因为没有对比就没有伤害。后来顺便用同机的 java11 跑了下——1 秒不到。

我从 ruby2.3 用到现在,这是第一次对 ruby 感到绝望。

难道每一个 ruby 项目的结局,都是用 java 重写吗?

试了一下 lz 的代码,在我的电脑上,打开 jit 8 秒左右,不开 jit 14 秒。

然后找了一段 Python 递归实现的斐波那契数列计算:

def fib(n):
    if n==1 or n==2:
        return 1
    return fib(n-1)+fib(n-2)

在 Python 2.7 下,执行时间是 23 秒。

我感觉这个例子并不能说明语言的性能,因为是递归实现的,只能看出不同语言对函数的处理。这种场景,编译型语言有优势,因为编译时候,编译器可以进行代码优化,把递归的调用给优化掉,所以会快。

我乱说的哈

给楼主另一个参考,我的环境在 wsl1 里,ruby2.7,没啥 jit 之类的,跑了 8s;python 3.9,跑了 28s,跑 python 的时候慢的我一度怀疑写的代码是不是死循环了

ruby3.0 jit 开启 5 秒,不开 10 秒。2.7 也是一样 go 500ms

计算型任务应该用 C 扩展。另外应该只优化 cpu 瓶颈。性能就没那么差了。

ps:可以参考网上一些 bench 数据

https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/yarv-python3.html https://github.com/kostya/benchmarks

别提了,用 PHP 8.0 跑这个 fib(40),直接像死循环一样长时间无响应了 😌

月经贴,鉴定完毕。

不止性能差距大,生态差距也很大,但是选择工具还是要看场景,在某些场景 ruby 自然也有优势的地方。

应该是 Martin Fowler 的企业应用架构模式里提到过,语言的分层选择。

对于底层的代码,由于后期变动少,对性能要求高,建议用中高级语言,如 C,java 这些。

对于近业务层的代码,建议使用语言表达更丰富,开发效率更高的语言,如 Ruby 之类。

这是我看了所有关于语言讨论里最认可的。

慢是事实,没啥好说的

动态语言肯定比静态语言慢了,Ruby 的比较对象应该是 Python、PHP、JavaScript。

性能也不是技术选型的唯一标准,要不然都用 C 好了,Java 就没必要诞生。

难道每一个 ruby 项目的结局,都是用 java 重写吗?

准确的说,99.99% 的 ruby 项目最后的结局,都是公司倒闭

Ruby 跟 nodejs 也有 20 倍的差距。

虽然语言有性能差距,但业务瓶颈很少会出在语言上,特殊性能场景选择合适的语言就够了,系统架构设计更关键

dddd1919 回复

更准确地说,100% 的任何项目最后的结局,都是公司倒闭。 凡人皆有一死……

Rei 回复

哪来的勇气和 js 比?

Peter 回复

happy cast?

唠叨一下。

设计数据密集型应用的作者,把开发开发分为这样几个方面

可维护性,可扩展性,可用性。

可扩展性,也分为横向和纵向。纵向是单机的性能能到多少。那么,语言性能影响的是纵向的扩展性。

设计应用,如果能横向扩展,几乎可以说是赢了。而横向扩展跟语言性能没什么关系。

再来看下我们的环境,首先是硬件越来越便宜,cpu 单核性能已经不那么容易增加了,得靠多核提升性能。

性能瓶颈多数是在存储这。我们拿 Facebook 的 memcache 集群为例,有篇论文是介绍他们怎么玩的。他们的优化都在处理横向扩展,如何应对突发流量,如何减少网络请求之类的。单机的优化很少。分布式里,似乎都不是很在意单机的能力。

再比如 discord 用 rust 替换了一个 erlang 的 orderedsort 实现,性能翻了多少多少。先不说没找到具体的测试用例、机器情况。他们的实现,erlang 的是遍历,rust 的是跳表。当数据量比较大的时候,算法的重要性远大于语言。顺便一提,erlang 有 ets,sort 是平衡树,c 实现,并发性非常好。

有的时候,看清楚问题是什么,当前的情况是什么,更重要。

问题到底是快速迭代还是极致性能(高性能,多数的时候大家还是用 c++,相对来说 java 差不少,外加加上 gc)。如何利用现有资源,比如分布式,大内存,算法。

语言不过是工具盒里的工具,性能不过是这些工具的一个属性。

大部分时候跟开发者关系更大一些,2 人写的 ruby 项目可能要比 10 个人的 java 项目跑得顺溜.....

piecehealth 回复

多谢,感谢指教!但是,想必您也能看出来,我这样写其实是就是为了跑 benchmark,故意让它的运算次数增加,可以跑得时间长点。否则按您优化后的算法,用任何语言跑都是几毫秒,用作性能对比不太直观。就好比两个人比赛爬楼梯,本来就是为了比体力,结果您在旁边说了一句“干嘛不坐电梯?”……

当然,优化算法往往对性能的提升是最大的,好代码与烂代码之间的性能差距何止千、万倍,这是用任何其它优化手段都做不到的。但是,现实中存在很多复杂计算,算法优化的空间有限,或者即使用了已知理论上最快的算法、用户体验上仍然无法接受,这就不得不想办法在其它方面(包括语言层面)压榨性能了。

至于 java 跑得快是不是因为对烂代码有优化,我对编译器知之甚少,不敢评价;不过,可能暂时持保留意见,原因有二:1、优化过的算法(好代码)只有几 ms,而烂代码 java11 实测 1 秒左右。以 java 编译器的功力,既然做了优化,为何不干脆优化到底?2、烂代码.net 4.8 实测 5s,.net 5.0 实测 2s,明显 5.0 的优化更好,但仍输于 java。而根据网上评测,.net5 的大部分算法 benchmark 小胜 java。如果这种烂代码很容易被优化,为何 2s 离 java 的 1s 仍有不小的差距?

tomanderson 回复

您说的没错,Ruby 是众多编程语言“爬楼梯”吊车尾的,这点毋庸置疑。如果需要处理“爬楼梯”场景,java 也可以略过,直接上 c c++ rust。

绝大部分应用处理的是“坐电梯”的事情,语言之间的差距是“走进电梯里的那一小步”,即使有差距也并不明显,主要看电梯调教的怎么样。

Ruby 慢这种事没什么好争的,但是“应用慢是因为 Ruby”这样的结论大部分情况是不客观的。

Peter 回复

说到心坎里。

不意外。

柱状图高度也直接表现了语言的舒适性。

省功不省力,省力就不省功。

快的东西,写起来就复杂,人牵就机器。 写起来舒服的东西,本质内部就复杂相对就慢,相当于机器牵就、服务了人。

27 楼 已删除

挺有趣的例子,我也知道 Ruby 慢,所以一般遇到瓶颈,必然还是要准备一下直接上 C 语言的,花了一小时,写了我的第一个 C 扩展 gem fibc

Fibc.fib_pure(4)  # processing time: 0.000034s
Fibc.fib(4)       # processing time: 0.000030s
Fibc.fib_pure(40) # processing time: 15.568888s
Fibc.fib(40)      # processing time: 0.511246s
需要 登录 后方可回复, 如果你还没有账号请 注册新账号