起因:昨天论坛老哥分享 Python 性能提升计划,其中一些涉及性能的讨论,27 楼老哥自己画了个图表看起来挺不错的。原贴链接
我个人是因为对 python 性能提升很感兴趣,对其他语言的语言性能比较也很感兴趣,有大版本更新的时候都会搞一些简单算法看看到底快了多少。这回看到这个老哥的图感觉挺不错的,该老哥称自己定期爬取 the benchmarks ganme 画出来的图,我感觉不如直接搞成一个自动维护项目,以后也省得手贱再自己动手跑了。
鼓捣了一下,大概思路就是基于 github 的分发特性,一个图片链接可以一直保持在最新数据,然后我在原版数据的基础上添加了两个 python 的 jit 解释器( pypy 和 pyston ),最后结果如下:
数据来源 https://benchmarksgame-team.pages.debian.net/benchmarksgame/index.html
=====================
几点说明:
1 、语言性能讨论一直是引战话题,我不是很理解为什么有人会在这个问题上被摸到 G 点,为了避免被喷,我先声明我这个不是严谨性能测试,只是基于开源跑分的爬虫并绘制图表而已。这个项目产生的原因是希望为需要的人提供一些定性结论(或者是定性结论的参考,毕竟它有可能不准),而不是严谨的定量结论。
2 、以前虽然跟别人讨论经常引用 the benchmarks game 的结论,但从来没仔细研究过这个网站。这次顺带仔细看了一下算法,我觉得它这些设计有几个可能本身也并不严谨。本身对比语言性能就是很难设计的一件事,理解。
3 、我在做这张图表的时候遇到一个统计学问题,由于一张图里要汇总多项测试的平均水平,一个最简单的想法是求平均耗时。但是需要考虑到一种极端情况就是,假设大部分测试都以非常短的耗时(比如 10 秒以内)完成,而个别测试极端情况的项目的总体时间消耗都很高(比如超过 600 秒),那么如果单纯做加和的话,整体的平均值会很大程度上取决于这个极端情况的结果,而削减了其他项目的影响力,为了避免这种情况发生,需要对数据加权。
我的方案是采用了如下图所示的运算得到最终结果,其中σ是方差,L 是常数,V 代表输出值。
大体思路就是,当一个项目整体测试时间较长的时候,我们会略微削弱这个项目的权重,让他不要影响那么大。同时考虑到如果用离散度描述整体测试时间的跨度,方差的比例差可能是一个非常大的数,所以需要进行某种压限,以确保权重最低的项目不会比权重最高的项目低出太多。算法是拍脑袋想的,有兴趣的欢迎看源码,或提意见。
4 、the benchmarks game 这个网站算是比较熟了,隔一段时间总会看到,但是说实话这次跑出来比例挺多反常识的,大概有以下几点:
我后面手动检查了一下数据,应该不是我写错了,确实测试结果加权平均算下来就是这样的,可能是由于这个跑分本身也没那么严谨,或者是特定语言在一些大量使用场景(比如正则)有让效率接近原生级的优化所导致的。
1
Leviathann 2021-05-16 06:07:25 +08:00
ghc 好强啊
|
2
woodfly 2021-05-16 06:16:00 +08:00
我觉得计算时间排名的时候,可以把各个单项测试的时间全部折算成 C++运算时间的倍数,然后求各个测试成绩的平均值作为该语言的成绩。
|
3
pkookp8 2021-05-16 06:39:09 +08:00 via Android
为什么 c++比 c 快,打破了我的直觉
|
4
hoyixi 2021-05-16 07:26:55 +08:00
Ruby 慢是早就存在的业界共识
|
5
noe132 2021-05-16 07:52:46 +08:00
js 经过 jit 优化后的代码效率非常高,和 native 属于一个梯度的,和编译语言性能不会拉开太大差距。当然要代码质量高,能够被优化才行
|
6
des 2021-05-16 08:36:43 +08:00 via iPhone 1
和我预期不一样
PHP 这么快,lua 这么慢? |
7
plko345 2021-05-16 08:42:21 +08:00 via Android
py3 比 py2 慢这么多?为什么,原理是啥?不是质疑,就是好奇
|
8
stephenyin 2021-05-16 08:49:16 +08:00
pyston 是啥?
|
9
voiyy 2021-05-16 09:04:13 +08:00
这个网站的数据并不是很准,rust 好多代码还是调用优化的 c 库,然后跟 c 手写的算法比跑分
|
10
SuperMild 2021-05-16 09:24:15 +08:00
符合我的印象,Java 比 Go 快一丁点,Go 比 V8 快一丁点,(但如果看内存占用,Go 会比 Java 少很多,也比 Node 少一些),Ruby 垫底,Python 比 Ruby 快一丁点(但也非常慢),但 Python 很多时候会通过调用 C 来提高速度,因此现实中 Python 还是很堪用。
|
11
aristolochic 2021-05-16 09:57:47 +08:00
@SuperMild 其实 Ruby 也是,而且应用广泛到在 Windows 上都不好装(因为要编译
毕竟 Ruby 和 Python 都用的是 libffi,而且 libffi 的 gem 封装 ffi 还可以适用于 JRuby 和 Rubinius ( libffi 用的实在太广泛了 除了拉原生扩展这个外援之外,动态语言还是比比什么抽象表达能力、类型标注什么的比较带劲 |
12
046569 2021-05-16 10:12:21 +08:00
https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/ruby-python3.html
以 LZ 引用的同一站点页面为例,Ruby 的速度是 Python 的多少倍: n-body:2.4 binary-trees:2.1 fannkuch-redux:1.9 fasta:1.4 spectral-norm:1.0 reverse-complement: 0.6 mandelbrot:0.6 k-nucleotide:0.6 pidigits:0.6 regex-redux:0.1 换句话说,Python 的最大优势项目速度是 Ruby 的十倍,而 Ruby 的最大优势项目速度是 Python 的二十四倍。 LZ 的加权算法导致结果有失公允。 |
14
hronro 2021-05-16 10:40:25 +08:00
确实楼主的加权合不合理还有待商榷,建议楼主可以做加权和不加权的两个版本
|
17
hronro 2021-05-16 11:03:46 +08:00
@des #16
JIT 对于纯 CPU 的运算提升还是很大的,但对于做 Server 这种大多在做 I/O 的提升就不大,影响 Server 性能的最大因素还是并发模型。而 PHP 的主要用途基本还是在做 Server 吧,所以可以理解成 JIT 对于 PHP 的主要用途提升不大。 |
18
yekern 2021-05-16 12:12:44 +08:00
两种权重比法啊
一种原生语言没有经过任何三方优化的编译器 jit 等等 一种加持各种优化 例如 PHP 早期的 hhvm 和 现在的 swoole 等等 python 的 pypy 等等 这就是一种参考,就像有人说过这么一句话 任何一门语言写的项目很少能达到语言本身的瓶颈 |
19
lovestudykid 2021-05-16 12:14:11 +08:00
接近底层的编译型语言,应该不会差出好几倍。如果差得多了,多半是程序写得实际不一样。
|
20
Building 2021-05-16 12:28:25 +08:00 via iPhone
Objective-C 不配拥有名字吗?
|
21
Cbdy 2021-05-16 12:59:50 +08:00
其实很合理,不是 Go 慢,而是 JavaScrip/V8 快,而做 V8 的人以前是做 JVM 的,就很合理了
|
22
zagfai 2021-05-16 13:24:38 +08:00
有啥意义??
|
23
missdeer 2021-05-16 13:36:45 +08:00
C++比 C 快出乎我的意料
最近我观察到 C++写的 notepad++在冷加载时往往很慢,但是 C 写的 notepad2 任何时候都很快(它貌似还有汇编级的指令集优化),我就以为随手写的 C 比随手写的 C++要快不少 |
24
xinyana 2021-05-16 14:26:26 +08:00 via Android
不太关心快不快,实现功能就行
|
25
chaowang 2021-05-16 14:43:33 +08:00
我也好奇,为啥 cpp 比 c 还快
|
26
des 2021-05-16 15:01:30 +08:00
|
27
toptyloo 2021-05-16 15:02:12 +08:00
java 不考虑启动 JVM 确实是很快的。
|
28
ipwx 2021-05-16 15:09:03 +08:00
@pkookp8 C++ 追求的 zero-cost abstract,template 大法超级给力。
举个例子,std::sort(vec.begin(), vec.end(), [&](const auto& lhs, const auto& rhs) { return lhs < rhs; }); 这里的比较器直接被 C++ 编译器内联了(开 -O2 )。如果是 C 语言版本的快排: bool fn(const void* a, const void *b) qsort(..., &fn); 函数指针丢进去。除非给每个函数写特殊规则(比如 qsort 是库函数说不定还有机会),一般函数根本没有内联优化的可能性。 ==== 再比如 C++ 17 的各种骚操作,例如: inline constexpr size_t pow2(int n) { return n == 0 ? 1 : 2 * pow2(n-1); } ... template <int N> if constexpr (pow2(N) > 1024) { ... branch A } else { ... branch B } 这段代码,pow2 和 constexpr 都是编译期就确定的行为,不会在运行时计算。 |
29
jhdxr 2021-05-16 15:11:08 +08:00
|
30
ipwx 2021-05-16 15:12:52 +08:00
|
31
3dwelcome 2021-05-16 15:18:26 +08:00 via Android 2
楼上打那么多字是认真的吗?
楼主链接有提供源代码,花十秒点进去看一下,就知道为什么 c++比 c 快了。 就是一个用了 simd 指令,另一个没用。 |
32
marcushbs 2021-05-16 15:23:04 +08:00
Ruby 3 jit 出来好久了,就没人想起来更新一下
|
33
matrix67 2021-05-16 15:24:47 +08:00
有没有最赚钱的语言排行,单位行数赚钱比
|
34
Jooooooooo 2021-05-16 15:53:47 +08:00
java 没有 jit 基本没法用
|
35
namelosw 2021-05-16 16:26:23 +08:00
Node.js 是个狠人啊…
|
36
LeeReamond OP @marcushbs 我这个项目可以自己加解释器,新增加语言不太行,因为需要自己实现算法,新加解释器比较简单,有需要欢迎 pr
|
37
LeeReamond OP @des 因为他原版已经画了这种图了,再画一遍意义不大,而且我希望我各个项目能提供直观结论,一眼能看出差多少倍那种的
|
38
LeeReamond OP @hronro 我的想法是如果做图的人自己都没有结论,还需要用户去自行筛选,那失去了简明的初衷,如果算法有问题可以修改算法,不应该做两个版本留给用户
|
39
akira 2021-05-16 17:30:46 +08:00
原生 快于 解释
|
40
chengxiao 2021-05-17 09:55:00 +08:00
.net 和 java 先把虚拟机去了再跟人比较快慢吧
|
43
lesismal 2021-05-18 18:28:49 +08:00
“Java 比 Go 快,而且领先幅度不小。”
—— 这是错觉。 全部对比应该是在这里吧: https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go.html 这是 go 和 java 对比的,大部分测试项 cpu 消耗其实各有千秋,最后两个测试项差距确实很大,但是,稍微修改下再对比: 以最后一项的 binary-trees 为例( go 耗时 12.80s ,java 耗时 2.48s ): go 代码修改: func inner(depth, iterations uint32) string { chk := uint32(0) tree := bottomUpTree(depth) // 这里从循环内移到了循环外 for i := uint32(0); i < iterations; i++ { chk += itemCheck(tree) } return fmt.Sprintf("%d\t trees of depth %d\t check: %d", iterations, depth, chk) } java 代码修改: for (int d = MIN_DEPTH; d <= maxDepth; d += 2) { final int depth = d; EXECUTOR_SERVICE.execute(() -> { int check = 0; final int iterations = 1 << (maxDepth - depth + MIN_DEPTH); final TreeNode treeNode1 = bottomUpTree(depth); // 这里从循环内移到了循环外 for (int i = 1; i <= iterations; ++i) { check += treeNode1.itemCheck(); } results[(depth - MIN_DEPTH) / 2] = iterations + "\t trees of depth " + depth + "\t check: " + check; }); } 我对 java 不熟,猜测 java 的编译器对 for 循环内局部对象甚至 tree 的构造过程做了复用优化 因为这种简单逻辑内的遍历,更成熟的老龄编译器可能会做更针对的优化,C++比 C 性能强主要就在于现代 C++编译器的优化,并且实际的业务场景,很少有需要这种频繁创建这样深度和节点数量的临时对象,几乎不会遇到这种小代码段级别的优化在实际业务中发挥太大性能优势,所以把遍历的部分构造放在外层时只对比 check 消耗时,性能差不多、在我机器上 go 略好。 语言、编译器有一个漫长的成长阶段,go 会越来越强的 |
44
lesismal 2021-05-18 18:35:12 +08:00
“Go 和 javascript 速度居然差不多。”
—— 这个没关系,毕竟 js 逻辑单线程,对多核心的利用能力肯定是不如 go 的,有人可能说 js 可以开多进程,但是 go 相比多进程还有内存共享、无需通信的优势,在面对更复杂业务功能时还是要强于 js 。所以单就服务端领域,抛开什么社区轮子或者说用 node 弱全栈配合写前端的优势,纯做服务器,go 还是比 js 强大太多,否则 node 爹当年也不会在熟悉了 go 之后感慨 node 不适合做服务器、而是适合辅助前端开发 |