1
6
2015
41

众编程语言间的 swapview 之战

本文来自依云's Blog,转载请注明。

swapview 起源于我很早之前看到的一个 shell 脚本。当时正在学习 Haskell,所以就拿 Haskell 给实现了一遍。为了对比,又拿 Python 给实现了一遍。而如今,我又在学习另一门新的语言——Rust,也拿 swapview 来练习了。相比仅仅输出字符串的「Hello World」程序,swapview 无疑更实际一些:

  • 文件系统操作:包括列目录、读取文件内容
  • 数据解析:包括简单的字符串处理和解析,还有格式化输出
  • 数据处理:求和啊排序什么的
  • 流程控制:循环啊判断啊分支什么的都有
  • 错误处理:要忽略文件读取错误的

因此,swapview 成为了依云版的「Hello World」:-)

感谢所有给 swapview 提交代码的朋友们

本文只是借 swapview 这个程序,一窥众编程语言的某些特征。很显然,编程语言们各有所长,在不同的任务下会有不同的表现。而且 swapview 各个版本出自不同的人之手,代码质量也会有所差异。

闪耀!那些令人眼前一亮的语言们

从运行效率上来看,C 如预期的一样是最快的。但令人惊讶的是,由我这个 Rust 初学者写的 Rust 程序竟然紧随其后,超越了 C++。

而原以为会跟在 Rust 之后的 C++,却输给了作为脚本语言存在的 Lua 语言的高效实现 LuaJIT(与 Rust 版本相当)。而且非 JIT 版本的 Lua 5.1 和 5.2 也都挺快的。Lua 这语言自带的功能非常少,语法也简单,但是效率确实高,让人又爱又恨的。

失望!那些没预期中的高效的语言们

没想到 Python 2 也挺快的,很接近 Go 了。PyPy 大概是因为启动比较慢的原因而排在了后面。Python 3 有使用两个版本的代码,Python3_bytes 把文件读取改为使用 bytes,仅在需要的时候才解码成 str。仅此之差,运行速度快了10%。可见 Python 的 Unicode 处理十分耗时,难怪 Python 3 在各种测试中都比 Python 2 要慢上一截。至于 PyPy3,怎么跑到那么靠后的地方去了呢……

Go 很快。至少比 Python 快。但也仅此而已了,不仅比 C++ 慢,甚至连 Lua(非 JIT 版)都不如。Go 语言版本虽然不是我写的,但我看过代码,感觉很原始。至少比 Lua 原始。看起来 Go 只不过是带接口和并发支持的 C 而已。而且,作为静态类型的编译型语言,却我却有一种很不放心的感觉。大约是因为我改动时发现传给 fmt.Printf 的参数类型和数目错了都不会得到警告或者错误的原因。而且我从来没见过 Go 编译时出现警告,对于还没入门的初学者写的、改过的程序,这样子不科学啊。早期我倒是见过 Go 报错了,但那只不过是编译器还不完善的表现而已。

传闻 NodeJS 很快。但至少它在 swapview 这种脚本中没能体现出来。正常版本比 Python 3 还要慢一点。而使用异步啊并行什么的版本还要慢上差不多三分之一,不知道怎么搞的。

编译型的 Chicken、OCaml、Haskell 都排在了一众脚本语言后边,虽然很可能是对语言本身不熟导致写出来的程序比较慢,但还是挺令人失望的。经过高手优化的 Haskell2 版本效率接近于 Python 3,但也到此为止了(因为不想使用 cabal 安装依赖,所以 Haskell2 没有参与这场对决)。我曾见过有人把 Haskell 代码优化到比 C 还快,但我宁愿去看汇编也不要去读那种代码……

Lisp 系(Chicken、Racket、SBCL(标注为 CommonLisp 的项)、Guile)也都挺慢的。不知道 LispWorks 之类的会不会快一大截呢。

预料之中的以及结果截图

Ruby 比 Python 略慢一点。

Java、Elixir 比较靠后。没办法,它们启动慢。也许以后我会出不考虑启动时间的版本。

以下是本文发表前的测试结果截图。其中 Erlang 版本因为有问题被信号所杀所以被扔在了最后。

测试结果截图

测试使用的是benchmark子目录中的 Rust 程序,使用cargo build --release命令即可构建。另外也可以使用 farseerfc 的 Python 脚本。

代码量

Elixir 代码量挺少的。Python、Ruby 也挺不错。Java 版本竟然跟 Haskell 一样。不管是 JavaScript 还是 CoffeeScript 都比较长,比 Java 还长。Rust 比 Python 长不少,但也比 Go 短不少。而 Go 比起 C、C++ 要短一些。最长的,除了我不了解的 Pascal,竟然还有因为程序出错还没有测试的 Erlang!如果不算按行读取的 line_server.erl 的放大,只有不到一百行,倒还不算多。

                  Elixir:   50
                   Julia:   51
           Python3_bytes:   53
                  Python:   56
                    Ruby:   56
                  Racket:   58
                    Bash:   63
                   OCaml:   65
          CommonLisp_old:   67
          CommonLisp_opt:   67
           Bash_parallel:   69
             C++14_boost:   69
                   Guile:   70
                 Haskell:   73
                 Chicken:   75
                    Java:   75
                  NodeJS:   76
                    Vala:   78
                Haskell2:   81
                       D:   86
                    Rust:   88
                   C++14:   89
                  CSharp:   91
                     Lua:   91
            NodeJS_async:   93
            CoffeeScript:   93
   CoffeeScript_parallel:   95
                     PHP:   97
           Rust_parallel:   98
                      Go:  103
                   C++11:  128
                   C++98:  141
                       C:  149
              FreePascal:  185
                  Erlang:  232

编译速度

这个比较非常粗糙,比如联网下载依赖也被算进去了。不过可以肯定,不算下载依赖部分的话,Rust 是最慢的!其次是 Haskell。标榜编译速度非常快的 Go 并不是最快的,和 C++ 不相上下(当然不知道代码复杂之后会如何了)。

0.36 C
0.60 FreePascal
0.80 OCaml
0.83 CoffeeScript_parallel
1.48 CSharp
1.67 Vala
1.68 Erlang
2.13 NodeJS_async
2.27 C++14
2.49 Go
2.53 CoffeeScript
2.90 C++11
3.01 C++98
3.23 Java
3.52 Racket
3.98 NodeJS
6.05 CommonLisp_opt
7.07 D
9.01 C++14_boost
10.41 Haskell
13.07 Rust
14.74 Chicken
15.37 Rust_parallel

结语

这个项目最初只是练习而已。后来不同语言的版本有点多,于是才演变成众编程语言的竞技。也就随意地测试了一下在给定需求下不同语言的表现而已。其实比较有意思的部分,一是使用正在学习的编程语言写作程序的新奇感、新知、新的领悟(这也是我的测试程序使用 Rust 编写的原因),二是对比不同编程语言的风格和对同样需求的处理方式。

各位读者对 swapview 有任何补充和改进,欢迎贡献代码哦~项目地址:https://github.com/lilydjwg/swapview

更新区

2015年1月9日更新:又收到了不少版本和改进,以下是最新的测试结果。很不幸地,现在已经跑得很快的 Erlang 在测试中又没反应被杀掉了。并行版的 Rust 的结果很不稳定,这次跑得好快!C++ 的除了 C++98 版的之外都到 Rust 前边去了。PHP 竟然比 LuaJIT 还要快!D 怎么到 PyPy 后边去了。

2015年1月9日的测试结果截图

2015年1月10日更新:C++ 版本继续改进,好多都超越 C 了,Rust 1.0.0alpha 的并列版本又快又稳定,Erlang 版本终于跑完了全部测试而没有出事,LLVM 版 D 快了好多。

2015年1月10日的测试结果截图

2015年1月18日更新:继续更新。又添加了若干语言,不过期待中的 Nim、Zimbu 以及传统脚本语言 Perl、Tcl 依旧缺席中。另外,正文也进行了更新,重新计算了代码量,添加了编译速度的粗略比较。

2015年1月18日的测试结果截图

Category: 编程 | Tags: python Lua Haskell go Rust LuaJIT 编程语言 | Read Count: 13998
Avatar_small
fc 说:
Jan 06, 2015 11:37:30 PM

咦NodeJS_async怎麼比NodeJS慢那麼多……我這邊跑的時候幾乎總是async的版本快一些的……

Avatar_small
fc 说:
Jan 07, 2015 12:08:49 AM

我剛纔跑了一遍的結果是這樣的:
https://img.vim-cn.com/de/1c80f6436ac546a882a04c56a1ced1f81f42d9.png

Channe 说:
Jan 07, 2015 12:10:00 AM

rust 目前有什么 IDE

MaskRay 说:
Jan 07, 2015 12:15:09 AM

比 Trabb Pardo–Knuth algorithm 好用!

coolwanglu 说:
Jan 07, 2015 12:36:50 AM

绝不承认c++慢,我来优化试试

Avatar_small
fc 说:
Jan 07, 2015 02:08:59 PM

4核8線程的本本……

Jex 说:
Jan 07, 2015 09:40:23 PM

。。。有什么好比的,C++里面直接字符串In-place重写、COW共享,就足以秒掉所有进行值复制的语言了,但这样欺负人家没意思啊。

JS那个最无语,还Parallel,Disk IO真的能Parallel吗?我好奇有没人去开Thread?

JS那个直接将字符串查找换成正则表达式性能就能提升好多,但这样就也没意思了。

我觉得这个性能比较的意义只在于,哪个语言的虚拟机+built-in方法速度快,这样VM/Runtime启动慢的就被K掉了。而built-in方法主要的又是字符串操作,性能瓶颈估计都在字符串操作上。

Avatar_small
依云 说:
Jan 07, 2015 11:27:41 PM

这还真不是 Disk I/O 呢。C++ 的并行版本确实快很多,而 Rust 并行版本标准差太大了,有时候特别快,有时候特别慢……

Jex 说:
Jan 08, 2015 02:54:01 PM

是的,本来我还想加句:「虽然异步IO算是和主程序Parallel,但只有在IO Latency够大的情况下异步IO才有意义吧,可`/proc`不是在Disk上的啊。」 后来一想我错了,磁盘文件会有Cache的,`/proc/`下的文件反而还是必须挨个读的-_-!

我估计这种情形下Node.js的异步IO能起到的效果很有限,为了跑分漂亮方法可多了,我将sync版本修改了一下,你再试哪个快?

Avatar_small
Jacky Liu 说:
Jan 08, 2015 08:10:58 PM

Guile 竟然这么慢,嗯 。。。

今天刚去了解了一下 Guile,惊觉要是它真做到宣称的那些功能的话,我将来恐怕会倒向 Emacs 也说不定

但是,看起来,速度还是个硬伤 。。。

lianjiefly 说:
Jan 08, 2015 09:03:07 PM

今天又来到了依云的博客,中间已经间断了三年了吧,大学里面的我几乎是天天在看博客的,也是天天写代码。后来毕业了,我找了工作做线材开发,但是跟计算机没半毛钱关系,要说有关系的话也是工厂里那些电脑以及网络也是我在维护而已。后来我恋爱了,也就慢慢没时间去关心代码什么的了,而后的一连串阴差阳错我来到了今天的政府单位上班,做的是财税方面的事情,更加远离了博客,但搞笑的是单位的网络维护又落到了我的头上,呵呵,命运么?在单位工作快两年了,今天又来到了您的博客,博客还是那个博客,风格还是那个风格,不同的是我变了,三年的女友分手了,而我,好象也变了,变得一无所有。。。

Avatar_small
依云 说:
Jan 08, 2015 09:26:17 PM

博客还是这个博客,然而人已不是当年那个人了。
毕业三年了么?看来跟我差不多大呢。

lianjiefly 说:
Jan 08, 2015 10:42:41 PM

应该差不多大吧,我是最晚的80后,我大学是看着你的博客过来的,学了不少知识,只是今天再看,多少有点感慨

Avatar_small
依云 说:
Jan 09, 2015 12:07:16 AM

应该不会比我还晚吧。

其实我这个博客(几乎是)纯技术的,所以也不会太受生活的影响(反正有想法就写,没有就晾着。遇到事情的话反倒是更容易折腾一些没什么用处的东西)。

mugbya 说:
Jan 09, 2015 03:12:39 PM

刚好看到一篇文章 : http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001397567993007df355a3394da48f0bf14960f0c78753f000

其中提到 计算密集型 vs. IO密集型 我觉得非常不错。

ps : 对比发现 c :149 行。 python2 : 58行

hmgle 说:
Jan 09, 2015 10:54:48 PM

Erlang 版本测试失败时什么原因呢?

Avatar_small
依云 说:
Jan 09, 2015 11:27:11 PM

我也想知道啊。现在连 crash dump 都没有了。只看到那个进程卡在 select(0, NULL, NULL, NULL 上……

hmgle 说:
Jan 10, 2015 12:02:33 AM

跑了一下, 我这边无法重现:
---------Results--------(Diff): KMinAvg Min Avg Max Stdev [K(5)-samples raw data]
C(100%): 94.19 94.02 95.81 101.05 2.14 [ 94.02 94.05 94.24 94.31 94.35]
Go( 66%): 150.87 150.39 154.90 164.73 4.24 [ 150.39 150.45 150.70 150.84 151.97]
Erlang(100%): 253.36 252.10 258.95 271.34 4.72 [ 252.10 252.38 252.41 253.12 256.78]
------------Fails-------------------
time_limit :

成付 说:
Jan 11, 2015 12:55:50 PM

我就是来测试一下新头像是不是已经生效了的~~~

jiyinyiyong 说:
Jan 12, 2015 09:33:14 PM

一行文本替换, 两个 replace 被我写成了 3 行.. 这个代码行数能小才怪.

waruqi 说:
Jan 14, 2015 10:09:58 AM

他这个测试demo本身就没啥可比性,语言解析损耗简直可以忽略,大部分瓶颈都在系统io上,像printf read最后还是调用了底层本地代码。。如果纯算法写个pi什么的 去对比。。排名肯定大改 = =

Avatar_small
依云 说:
Jan 14, 2015 11:32:24 AM

请见我在 GitHub 上的回复: https://github.com/lilydjwg/swapview/issues/67#issuecomment-69864112

defia 说:
Jan 16, 2015 07:39:57 PM

首先,在我虚拟机和香蕉派上跑,试着跑了一下go和python3版本。。。都是total 0.0Kib。。根本跑不出正确的结果

然后香蕉派居然比虚拟机跑的还快50%。。。一个1ghz的cortex a7 vs 3.5hgz的haswell,完全说不过去啊。。可见瓶颈完全不在cpu。。。

defia 说:
Jan 16, 2015 07:43:23 PM

忘了说,。。香蕉派和虚拟机对比跑的是go版。

Avatar_small
依云 说:
Jan 16, 2015 08:00:19 PM

不是跑不出来正确的结果,而是你没有使用 swap 所以是那样的结果。你可以把 Swap: 改成 smaps 文件里的其它字段来跑。

至于瓶颈在哪里,我也不知道。go 应该有 profiling 工具吧,你看看就知道了。在我这里,profile 结果说 Python 3 版的瓶颈在系统调用和字符串处理上。

Endle 说:
Jan 18, 2015 05:54:05 PM

Lua 这么给力啊,我都想去学 Lua 了

pynix 说:
Feb 08, 2015 02:34:23 AM

nodejs async和这些有毛关系,异步又不能减少io时间。。。。

Matthew 说:
May 05, 2015 04:26:59 PM

找了一圈,没看到 Perl 的。。

Avatar_small
依云 说:
May 05, 2015 06:57:53 PM

在这里: https://github.com/lilydjwg/swapview/blob/master/Perl/swapview.pl

Matthew 说:
May 05, 2015 08:28:42 PM

瞅见了。。

帖子里没有看到 Perl 的跑分。。

Avatar_small
依云 说:
May 05, 2015 09:36:32 PM

嗯,因为是后加的。

Mike 说:
May 18, 2015 09:34:46 PM

依云,欢迎加入Rust高端群 。等你回来。
Rust语言深水区 458795031

georgeliu 说:
Jul 04, 2015 01:19:18 PM

试试clozure cl

cholerae 说:
Aug 13, 2015 12:43:05 PM

我决定捍卫一下Go的尊严……

hetk 说:
Oct 01, 2015 06:22:27 AM

能不能在程序开始的地方和结束的地方加代码统计运行时间?忽略启动速度


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com