瞎扯淡 好像 Ruby 的性能问题始终是个大问题啊!

discover · 2013年12月20日 · 最后由 kgtong 回复于 2014年02月13日 · 18215 次阅读
本帖已被管理员设置为精华贴

http://codeforces.com/blog/entry/10024 看这个对比,吓了一跳!

哎,爱它只能包容下;

我觉得这种测试没实际太大的意义。

这纯属测试语言的所谓性能问题,要是用 c 和汇编的话,会更快的。

#2 楼 @tankerwng 随便的一种其他语言都能比它快几个数量级,这就是不应该是忽视的问题了。

这个测试的都是 win 下的 ruby i386-mingw32 linux 下应该比 python 快的

差距确实大,ruby 代码是怎么写的呢?

这种测试没意义的,而且竟然还用 windows 下的 32 位 ruby 测试

#7 楼 @redvoilin 这是个算法竞赛的网站,他们的评测系统编译环境就是这么安装的。

擦!真是个大问题啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

性能不行可以堆机器 ruby 的最大优势在于能快速出产品 技术本身的价值为 0 产品拿出来赚钱才有价值

现在手机都 4 核了,瓶颈一般在磁盘 IO,网络 IO,数据库。

常见的代码写得太矬把责任推给语言

是不是 ruby 的性能问题每次都要被拖出来鞭尸?

这种比较网路上有很多,以下这种是比较 OO 问题解决的: http://blog.dhananjaynene.com/2008/07/performance-comparison-c-java-python-ruby-jython-jruby-groovy/ 当然又跟你的有差别,不过重点还是“参考就好”。选语言时碰到瓶颈再担心这个吧。

Ruby 要完!!!

矬的代码,用 C 和 ASM 都能跑出锉的性能。

Ruby 原来这么慢,还好服务器有 16 核 32G,要不然完蛋了

Ruby 最大的问题是没几个人能写出正确的 Ruby parser,一个星期都不行 ...

当你打不开 ruby-china 这个网站的时候,再去怀疑 ruby 的性能……

#19 楼 @fengzhe 小时候我总是纠结上清华还是上北大。长大了才知道其实我想多了。 😆

当你遇到性能问题时,要做的不是换语言,而是换人...

想立竿见影提高 ruby 的执行效率很简单,把那堆 ir 保存下来,直接读字节码执行就行了。

匿名 #26 2013年12月20日

#20 楼 @bhuztez 写一个 parser 倒不至于吧,看看王垠的这个rubysonar,虽然还不完善,但是因为王垠以前开发过 python 的静态分析工具 pysonar,非常强大,所以我相信不要多久,rubysonar 也会变得很完善的。这可不只是一个 parser 哦

C 与类 lisp 是两个天,有啥对比的.对比那是蛋痛的人。

28 楼 已删除

windows 下的 Ruby 基本没有参考价值,是用 msys 编译的 而 Python nodejs 都有用 msvc 的版本

#28 楼 @bhuztez 王垠比你会吹牛

#28 楼 @bhuztez智商如此拙计竟然也能拿到phd 的网站地址

这贴要不要加精呢!

#33 楼 @huacnlee 这贴的 #19 楼 @fengzhe 评论非常有代表性,个人表示赞同。B 大 @bhuztez#28 楼 的回复可谓击中要害......

但是这个帖子本身的题目,以及其他吹水的回复,让我觉得还是不要加精了吧,毕竟这种帖子每个月都会来一次。

#30 楼 @ShiningRay

王垠吹牛就三招:

  • 我见过非常非常牛 X 的人物,我和他们谈笑风生
  • 我见过各种傻 X,我比他们牛 X 多了
  • 我很牛 X,我很牛 X,我就是很牛 X

@discover 不知道楼主以及各位跑过这个测试没? 在我的 iMac 2011 的机器上,我跑了一下 Py 和 Rb 的测试,结果和他给的结果很不一样啊: 为了节约时间,我把 Py 和 Rb 中的 10000000(7 个零)都改为 1000000(6 个零):

Python 的结果:14682.005000 Ruby 的结果:12301

Python 和 Ruby 在一个数量级,而且 Ruby 还快一点。明显与他的测试结果不符啊。 Py version: 2.7.5(Mac OS 自带的) Rb version:2.0.0-p353

是我机器的 Python 有问题吗? @luikore@lgn21st

#34 楼 @lgn21st …………毫无调查……张口就来的帖子怎么就能 击中要害

#35 楼 @bhuztez 你这不也不比他高明多少么……至少人家这方面还有干货…… 何况自大到他那种程度的人怎么会优先考虑用别人的代码……

38 楼 已删除

用 2.1.0-preview2 跑了下,比 2.0 快了不少了,已经比 python3.3 快了

$h[pos], $h[j] = $h[j], $h[pos]

写成

tmp = $h[pos]
$h[pos] = $h[j]
$h[j] = tmp

就能快不少 优化前:68091 优化后:58694

#36 楼 @skandhas 对啊,之前的 jjyr benchmark 也是类似的结果 http://ruby-china.org/topics/12688

很奇怪啊,几乎一样的实现 (Python 只是没有 direct threading),Ruby 1.9/2.0能快很多

#40 楼 @bhuztez 我奇怪的是他那个评测网站的结果很离谱啊。Py 的结果是 10 多倍快于 Rb,但是实际测下来却完全不是那回事啊。Ry 与 Py 性能在伯仲之间呢。难道他用 100,000 测的 Python,用的 10,000,000 测的 Ruby? (阴谋论?哈哈哈)

#41 楼 @skandhas 可能是因为在 Windows 上测的 ...

还是 @luikore 说的对,不能被这种评测给坑了。 还得自己跑跑并看看他写的评测代码,这样才能心中有数啊。

To @discover:看了大家的评测,现在应该不会下一跳了吧;)

#42 楼 @bhuztez 恩,有可能啊。下周在公司用个 Windows 测测。看看 Py 在 Windows 下出奇的快不;)

#37 楼 @Kabie 不需要调查呀,我对 @bhuztez 对王垠的看法十分认同,不直接评论王垠本人只是为了不让自己卷入国内其他社区各种神话他和丑化他的闹剧中而已。

#44 楼 @skandhas 很可能是 Ruby 出奇的慢吧

#46 楼 @bhuztez 嘿嘿。Rb 的测试昨天在公司测过,和 Mac OS 下的速度基本相仿。没测过 Py 在 Windows 下的表现,所以对结果很奇怪;)

#44 楼 @skandhas #29 楼 @ShiningRay #36 楼 @skandhas #42 楼 @bhuztez 难得大家还有雅兴关心一下 windows,那我运行一下算了,heap2.rb用了国外一个看不下去黑 ruby 的大神的 pull request。

ruby 2.0.0p353 (2013-11-22) [i386-mingw32]

C:\git>ruby heap.rb
Done in 424610

C:\git>python --version
Python 2.7.2

C:\git>python heap.py
Done in 154276.321575

C:\git>ruby heap2.rb
 93.679000   0.016000  93.695000 ( 94.284052)

结论是 Python 2.7 154 秒 vs Ruby 2.0 94 秒

update:为了公平,还特意升级到了 python 最新的 2.7.5.6 ActiveState 版本

C:\git>python heap.py
Done in 154711.043911

C:\git>python --version
Python 2.7.5

显然新版本更慢一点。。

轮到 Python 要完了。

#28 楼 @bhuztez 感觉 ruby 语法规则不像 python 拆的那么开啊 都写到一个文件里面了 话说 python ASDL 是个啥 介绍好少...

#48 楼 @ericguo 这就是我很奇怪他的结果。很有可能就是 100,000 测 Python,用 10,000,000 测 Ruby。或者说原作者就是超级 Ruby 黑。

#48 楼 @ericguo

Ruby 代码主要就是 a, b = b, a 这里手工展开所以才比 Python 2.7 快了,

我去掉了个 0,实在太久了,等不起。

$ python --version
Python 2.7.5
$ python3 --version
Python 3.3.2
$ pypy --version
Python 2.7.3 (352c78d2e80f4a812ae1d8cdbe8c01a7f2e6fbc0, Aug 19 2013, 10:43:46)
[PyPy 2.1.0 with GCC 4.8.1 20130603 (Red Hat 4.8.1-1)]
$ ruby --version
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]
$ python heapsort.py 
17.9021840096
$ python3 heapsort.py 
30.28932676600016
$ pypy heapsort.py 
0.749085903168
$ ruby heapsort.rb 
 23.450000   0.010000  23.460000 ( 23.611715)

代码改成一致的实现,不能因为一边有更快的写法就用更快的写法。

from __future__ import division, unicode_literals, print_function

import timeit

try:
    range = xrange
except NameError:
    pass


def pushDown(h, pos, n):
    while 2 * pos + 1 < n:
        j = 2 * pos + 1

        if j + 1 < n and h[j+1] > h[j]:
            j += 1

        if h[pos] >= h[j]:
            break

        h[pos], h[j] = h[j], h[pos]
        pos = j

def heapsort(size):
    heap = list(range(size))

    for i in range(size//2, -1, -1):
        pushDown(heap, i, size)

    for n in range(size-1, 0, -1):
        heap[0], heap[n] = heap[n], heap[0]
        pushDown(heap, 0, n)

    for i, e in enumerate(heap):
        assert i == e, "Array not sorted"

if __name__ == "__main__":
    print(timeit.timeit("heapsort(1000000)", setup="from __main__ import heapsort", number=1))
require 'benchmark'

def push_down(heap, pos, n)
  while (2 * pos + 1) < n
    j = 2 * pos + 1

    if (j + 1 < n) and (heap[j + 1] > heap[j])
      j += 1
    end

    break unless heap[pos] < heap[j]

    heap[pos], heap[j] = heap[j], heap[pos]

    pos = j
  end
end

def heapsort(size)
  heap = (0...size).to_a

  (size / 2).downto(0) do |i|
    push_down heap, i, size
  end

  (size - 1).downto(1) do |n|
    heap[0], heap[n] = heap[n], heap[0]

    push_down heap, 0, n
  end

  raise "Array not sorted" unless heap.each.with_index.all? { |element, index| element == index }
end

puts Benchmark.measure { heapsort 1000000 }

#51 楼 @skandhas 也可能是 Python 用了 pypy ...

#47 楼 @skandhas 当处理的数量级达到一定大的程度的时候,确实会出现对性能的影响

我的测试结果

$ ruby -v
ruby 1.9.3p448 (2013-06-27) [i386-mingw32]

# 七个零的结果
$ ruby heap.rb
Done in 893057

# 六个零的结果
$ ruby heap.rb
Done in 17729

作为对比的 python 结果

$ python --version
Python 2.7.6

# 七个零的结果
$ python heap.rb
Done in 179158.244072

# 六个零的结果
$ python heap.rb
Done in 14071.505574

作为对比的 java 结果

$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

# 七个零的结果
$ javac Heap.java
$ java Heap
Done in 1272

# 六个零的结果
$ javac Heap.java
$ java Heap
Done in 138

跑分前均未进行热身,

机器配置

戴尔笔记本
CPU Intel(R) Core(TM) i5-3210M CPU @ 2.5.0GHz 2.50 GHz
内存 16.0GB(15.9 GB 可用)

其实 java 来测试字符串拼接也有类似的情况,在 某个数字之内(不记得具体数字了,大概是 5000)用 + 来拼接字符串毫无影响,但是一旦超过这个数字哪怕就多一次花费时间就上去了好几倍

#48 楼 @ericguo 我没测这种,我测试的自己的 rails 应用的响应速度,在 windows 上唯一性能令人满意的是 jruby 让他用 jruby 试试么

#56 楼 @ShiningRay 快跑我的代码,看看结果如何

#52 楼 @bhuztez 这个结果也是在伯仲之间。 #54 楼 @ywjno 嗯。我在意的是他的 Py 结果的数量级。按照咱们几个人的测试来看,Rb(2.0)和 Py 的性能数量级应该在伯仲之间。而明显不是 他的测试说明的 10 多倍。

#56 楼 @ShiningRay

作为对比的 jruby 结果

$ jruby --version
jruby 1.7.6 (1.9.3p392) 2013-10-22 6004147 on Java HotSpot(TM) 64-Bit Server VM 1.7.0_45-b18 [Windows 7-amd64]

# 七个零的结果
$ jruby heap.rb
Done in 1405441

# 六个零的结果
$ jruby heap.rb
Done in 7037

#58 楼 @skandhas 此处用 pypy 有约 20 倍速度提升。这样来看,V8 好像也不算快么 ...

#60 楼 @bhuztez 不知有没有 rbrb 项目?

Real world Ruby:

首先转到 cpp 的目录

g++ -O4 heap.cpp -o heap

然后把 heap.rb 改写成

require "fiddle"
require "fiddle/import"

module Cpp
  extend Fiddle::Importer
  dlload '../cpp/heap'
  extern "int main(int,char**)"
end

Cpp.main 0, nil

#41 楼 @skandhas 好吧,我也觉得是 Python 少写了个 0

#61 楼 @skandhas 重新算了下

java 195 javascript 用 Mozilla 的引擎,速度上和 V8 应该区别不大

js -j 641 js -a -m 1741

在对比 Pypy 的速度

可以认为 ruby 被多加了个 0

#64 楼 @skandhas 事实上都是这么搭积木的啊。这类事情用 py/ruby 写起来和 cpp 差不多,而且在 cpp 肯定能找到实现,直接找个现成的调用好了... 这才是 idomatic ruby

#66 楼 @luikore 嗯,十分赞同! #65 楼 @bhuztez 看来,Ruby 这次是中黑枪了。哈哈

哈,用 python 的人现在一般都不在意 python 的性能了,1 是 cpython 性能不会有大的改变了,2 是争论这个也累了。好在 python 现在有 jit 的 pypy, cython 实在是一个利器,numpy 等科学库有大量的优化的数据结构可用,现实中说 python 有性能问题的,不是你业务比肩 google, 就是自己钻研的不够了

#28 楼 @bhuztez 一星期写出来也有区别,纯手写?那基本没有一个人能做到,手动推导 lr1 项集和用机器推导比没有任何智力上的更多的的乐趣。用工具?如果用工具,纯文法一周也并非不可能,但是要做到什么形式的 ir?线性还是图形还是树型异或是组合型?这就不是一周的问题了。 任何动态语言都可以推出类型来,数理逻辑里面有一部分和它很有关系。

#69 楼 @rasefon 只要不准看现有的 Ruby parser 是怎么写的。就算你用现成的工具生成也很难啊,你自己试一下就知道了么,除非你整天就是在写各种诡异的 parser 的

#66 楼 @luikore

Cython 还是略坑

from libc.stdlib cimport malloc, free
from cython import sizeof

cdef pushDown(int *h, int pos, int n):
    cdef int j

    while 2 * pos + 1 < n:
        j = 2 * pos + 1

        if (j + 1 < n) and (h[j+1] > h[j]):
            j += 1

        if h[pos] >= h[j]:
            break

        h[pos], h[j] = h[j], h[pos]
        pos = j


def heapsort(int size):
    cdef int i, n
    cdef int *heap = <int *>malloc(sizeof(int)*size)

    for i in range(size):
        heap[i] = i

    for i in range(size//2, -1, -1):
        pushDown(heap, i, size)

    for n in range(size-1, 0, -1):
        heap[0], heap[n] = heap[n], heap[0]
        pushDown(heap, 0, n)

    for i in range(size):
        assert i == heap[i], "Array not sorted"

    free(heap)

#70 楼 @bhuztez 不看现成的也得有文法标注吧,一周写出来很困难,为什么要一周写出来呢?

#72 楼 @rasefon 写不出来说明很难啊

#73 楼 @bhuztez 我觉得应该算是复杂不是难吧。复杂在有些情况要向前看两个词素,有些情况要回溯词素,即使有工具支持也非常麻烦。我自己觉得实在没必要把文法设计成这样。我喜欢简洁的文法。

#62 楼 @luikore 赞,真实的库就是这么来的。

#74 楼 @rasefon 这就是为什么我放弃 Ruby 转而自制语言了 ...

#76 楼 @bhuztez 这年头大家都想自己做语言了?……


 └─> rvm list known
# MRI Rubies
[ruby-]2.0.0[-p353]
[ruby-]2.1.0-head
ruby-head

# JRuby
jruby-head

# Rubinius
rbx[-2.2.1]
rbx-head

# Ruby Enterprise Edition
ree[-1.8.7][-2012.02]

# Mac OS X Snow Leopard Or Newer
macruby-head

Rubinius 如何 呀?

#66 楼 @luikore 是不是拼写错了?idiomatic ruby ?

#79 楼 @googya 嗯,少了个 i

#19 楼 @fengzhe 不能同意你更多!

#35 楼 @bhuztez 哈哈哈,我要给你 32 个赞~

#71 楼 @bhuztez PySonar 实在是太素了,就是个简单的 Abstract Interpretation 做类型计算,加个扫栈去环,parser 直接调用的 Jython 都不用写,真没感受到是什么惊天动地的创举... 但如果加上考虑真实世界是有 thread 和 continuation 这两种平行栈和树状栈的状况,那点代码就不够使了... 而且这种东西设计时先把类型运算的公理列出来吧 (老子从来不看 ICFP 论文集!), 但 PySonar 只能从代码中看做了什么 (还是 Java 的一下就没胃口了)... M$ 研究人员写过利用自动化定理证明来生成 reduction rule 集合的 (把 intel 手册输进去以后就能获得无敌编译器了), 显然 PySonar 没到那个程度。

动态语言做静态分析的可多了,SBCL, Cython, Groovy, Mirah, Diamond-backed Ruby... 各有它们的优缺点。例如 SBCL 就是分析得了就是一条龙,分析不了的就是一条虫。但这些东西都做了巨多的工作,design decision 中也考虑到了各种各样扭曲的 case, 和玩具不是一个层次的...

#83 楼 @luikore 实现方法多老土都没关系,结果不可靠还拿出来吹就没意思了。

#83 楼 @luikore Cython 基本等于不能分析,大部分时候你要把黄色干掉都需要标注类型,但是也没办法,这是 Python 语义决定的。

#85 楼 @bhuztez "基本等于不能分析的东西"也是不好做的,例如精确度和编译时间要做取舍,要有语言的使用经验或者精确 spec 才能添加 edge case 的处理,很多东西不是用了什么特别的算法就可以闹革命的。我觉得关键还是类型系统的设计,哪天有人把现在动态语言的类型系统解构成另一个颠覆性的类型系统之后,静态分析可能就有出路了...

现在还是运行时分析最简单,效果最好:inline method cache / trace based analysis .静态分析光走一遍线性的 peep hole 字节码重写就够了...

直接调用 C 来处理,当然更快的哈.python 一样可以调 C 的,语言的性能在于怎么用,谁来用。

#86 楼 @luikore 不需要设计啊,Erlang 就自带静态分析工具的。

#88 楼 @bhuztez erlang 的静态分析是针对它的类型系统和 immutable / process 内存管理等特性的,完全没法应用到 python/ruby 上啊

#89 楼 @luikore 所以,直接改用 Erlang 就可以了 ...

#86 楼 @luikore 昨天我洗澡的时候也在想这个问题,想到 2 点: 1.类型闭包,即可相互转型的类型作为一个闭包,最后合并同类项。 2.类型转化是由用户决定的。

#91 楼 @rasefon 你这个说的是弱类型吧

#92 楼 @bhuztez 是啊,强类型直接就可以解析了,现在已经可以做得不错了。

#93 楼 @rasefon 不是的,比如 Python

class A(object):

    def x(self):
        return 1

A.x(self)self 的类型必须是 A 么 ?

完全没有必要 A.x(None) 也是可以的嘛

#94 楼 @bhuztez 我没看过 py 的语法,不过应该可以 ruby 差不多。我前面理解你说的弱类型是指非静态类型,大概我理解不对。 这里的 self 我字面理解一下,应该是调用时才能决定的类型。

#95 楼 @rasefon 最大的问题是没法确定类型正确的边界在哪里,有很多时候 Python 代码是依赖异常来工作的,你不能说会导致异常就说传进去的值的类型不对。

#96 楼 @bhuztez 我打个比方,没法确定一个确定元素的时候,可以把范围扩大,确定一个集合,然后把集合里面的元素按照可能性排序,然后遍历,是不是可以提高效率?

#97 楼 @rasefon 要么就是我没理解你在说什么,要么就是你没理解类型推断是怎么实现的

#98 楼 @bhuztez 等我试试看回头再说明一下。

#98 楼 @bhuztez 其实编译里面的很多想法都很类似,比如类型推断用占位符,就和代码生成里面的回填地址想法差不多,我的想法就是回填时候并非每次都需要确定的东西。

#91 楼 @rasefon

  • 静态类型:变量名有类型,值不带类型信息
  • 动态类型:变量无类型,值带类型信息
  • 强类型:检查类型,在编译时 (例如 Haskell) 或者运行时 (例如 Lisp, Ruby)
  • 弱类型:不检查类型,例如 ASM, 早期的 PHP 和 Basic (用错类型就会 segfault)

常见静态语言都带有一定的动态能力 (如 C++ 的运行时多态), 但一些要求严格的代码库,如 OpenJDK 会要求多态都在编译时决定好。

但是总有一些人想混淆弱类型和动态类型 (因为他们主张的静态类型没动态类型好听,但强类型比弱类型好听)...

静态分析做推断有两种极端:一种是什么都不丢保持精度 -- 很容易类型闭包信息爆炸内存溢出,另一种是降低精度用比较粗泛的类型去描述复杂类型运算的结果 -- 很容易什么效果都没有。现在的分析都是在这两种极端中找平衡。

#101 楼 @luikore 静态分析没什么类型闭包吧,差不多就是在编译期关于类型的 Prolog ...

比如

fun f(a, b) { return a+b; }

改写成

f(A, B, Ret) :-
  +(A, B, Ret)

类型检查因为有上下文,倒是没有什么问题。因为显然满足 close-world assumption?

静态分析就意味着你要给出所有可能的情况。这个很容易就变成不可能的任务了。

#101 楼 @luikore 你那个是无类型吧。弱类型是有隐式类型转换。只有这种情况才有类型闭包一说吧。

#101 楼 @luikore 这种强弱分法没意义,除了汇编全是强类型。

#103 楼 @bhuztez #104 楼 @rasefon

那说明你要说的东西不适合用强弱类型来表述。反正这个概念已经非常混乱,没法取得一致意见了...

看原文后面的评论吧。。

http://codeforces.com/blog/entry/10024#comment-154855

Hi!
Unfortunately, the Ruby implementation is not only very non-idiomatic in terms of code, but it does abuse the assignment operator to perform the exchange. This is why it is slower in the benchmarks. You'll find it performs way better if you take this into account.
Here's a pull request: https://github.com/MikeMirzayanov/binary-heap-benchmark/pull/10
In my testing, this version outperforms Python3 by a factor of 2. No clever tricks added.

对于做网站,我有一个偏激的想法:如果缓存做到极致,任何语言都是一样的。因为相当于在跑静态页面了。

Ruby 2.0 我电脑上约 330 s,和该网站比较接近,但 Python 2.7 我测是 120 s 左右,该网站结果应该有问题,另采用最新的 Ruby 2.1 32 bit 结果为 110 s 左右,Ruby 2.1 64 bit 结果为 100 s,稍快于 Python,以上是 Windows 下结果;在 Linux 下,Ruby 2.1 64 bit 结果为 62 s,Python 2.7.5 结果为 84.5 s,均快于 Windows 平台,且测试用的 Linux 在虚拟机中。Ruby 2.0 当 N 较小时快于 2.1 版,但 N 较大时慢于 2.1 版。Ruby 2.1 对于此测试性能已稍胜 Python 2.7。

109 楼 已删除

最近是这样的感觉: 机器上面的 agent 用 C/C++ 开发,要求占用内存少,跟内核打交道 后端业务逻辑 server 用 java 和 go 开发,要求网络编程和多线程编程方便 web 用 ruby 编写,快 对于后端模块如何对性能没要求,用 ruby,方便

#108 楼 @leaz python 版本不一样吧。

@yangff 对比下,没必要和那个网站一样

114 楼 已删除

实际的效率与程序的结构有很大的关系,这个测试作为参看吧

116 楼 已删除
117 楼 已删除
需要 登录 后方可回复, 如果你还没有账号请 注册新账号