12
24
2014
39

Rust 初体验(真快!)

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

最近又看到 Rust 的相关东西了,入门指南也写得挺不错的。这语言我越看越喜欢。

Rust 的目标是系统级编程,就像 C 那样,快速高效。同时它继承了 Haskell 的诸多特性,包括其类型系统(包括类型类和类型推断)、模式匹配。而读写起来,又和 Python 差不多简单明了。简直是把这三种语言的优点全学到了!(当然 Rust 不仅仅受到了这几种语言的影响啦。)

当然,要体验一门编程语言,最好的方式就是使用它。于是我拿它实现了我最开始用来练习 Haskell 用的 swapview 程序。

swapview 的功能是,读取/proc下每一个进程目录下边的cmdlinesmaps文件,得到其命令行和 swap 使用量,然后排序、格式化,并打印出来。

Haskell 第一版实现挺慢的:

swapview  1.27s user 0.26s system 98% cpu 1.555 total

我随手写了个 Python 版,效率翻了一倍还要多!很令人惊讶的呢。作为解释执行、还一直被认为很慢的 Python 竟然在没有任何优化的情况下就超过了编译型的 Haskell:

swapview.py  0.35s user 0.18s system 97% cpu 0.548 total

后来在 IRC 上遇到一位懂行的人,用了不少手段优化,最终得到了 Haskell 第二版:

swapview2  0.42s user 0.15s system 98% cpu 0.583 total

比 Python 版略慢。

才学 Rust 没几天,我对 Rust 比对 Haskell 更不熟。花了不少时间查阅文档、调整代码。不过因为之前的 Haskell 基础,也没遇到太大的困难。结果如下:

swapview  1.84s user 0.15s system 97% cpu 2.038 total

呃呃呃,怎么比 Haskell 版本还要慢上不少啊?

本来是找 profiling 方法的。翻着 rustc 的 man 文档,看到了-O选项,眼前一亮——我忘记告诉编译器要优化了!这是启用优化的结果,比 Python 版又快了一倍:

swapview  0.10s user 0.13s system 96% cpu 0.237 total

真棒呢~

不过很遗憾的是,它的格式化函数的第一个参数必须是字面量,连常量都不行。因为那是个宏,要在编译期解析格式……另外似乎也不支持现在连 JavaScript 都已经支持了的 generator(只支持 iterator,得先写一个 struct 才能用)。

PS: Rust 的文档挺赞的,和 Python 的一样有 JavaScript 实现的搜索功能,比起 Nimrod 和 Zimbu 的好用太多了。

PPS: 谁有兴趣可以贡献个 Go 版、C 版、C++ 版、LuaJIT 版什么的=w=


2014年12月25日更新:目前的结果是(运行时间):Rust < LuaJIT < C++14 (gcc 4.9.2) < Lua 5.1 / 5.2 << Python 3 < Haskell <<< OCaml < SBCL。手动测试的。有空我再写个好点的自动测试程序。

2015年1月6日更新:添加了更多的编程语言,以及更准确的运行时间测试,请见新文章编程语言对决——战场:swapview

Category: 编程 | Tags: Haskell 编程语言 Rust | Read Count: 12898
mugbya 说:
Dec 24, 2014 09:28:54 AM

你的python里面那个通用?

我安装了myutils,但是还是有如下错误呢?

File "./swapview.py", line 7, in <module>
from myutils import filesize
ImportError: cannot import name 'filesize'

Avatar_small
依云 说:
Dec 24, 2014 10:58:05 AM

你从哪里安装的?myutils 在我的 winterpy 仓库里的啦。
另外 Haskell 版本依赖我的 myhaskells 里的东西。

mugbya 说:
Dec 24, 2014 12:25:00 PM

我用 easy_install 安装的。 难怪

你的winterpy 在你的源里 ?

Avatar_small
依云 说:
Dec 24, 2014 12:32:08 PM

不在。你去 https://github.com/lilydjwg/winterpy 里找到并把 myutils.py 下载回来和它放一起就可以了。

winterpy 里边乱七八糟的东西太多了……

mugbya 说:
Dec 24, 2014 12:58:54 PM

还依赖了nicelogger ...

不过我什么都没执行出来 ...

PID SWAP COMMAND
Total: 0B

Avatar_small
依云 说:
Dec 24, 2014 01:36:18 PM

你的系统没有用到 swap 啦~

我刚把那些依赖去掉了。

mugbya 说:
Dec 24, 2014 03:08:53 PM

额,我才记起,我没划swap出来 -_-

coolwanglu 说:
Dec 24, 2014 03:59:51 PM

可是rust代码是python两倍长,不见得合算。

Avatar_small
依云 说:
Dec 24, 2014 04:40:37 PM

因为代码结构不一样啦。Rust 使用花括号也会多占一些行。

Avatar_small
fc 说:
Dec 25, 2014 02:44:49 AM

我這邊Rust版本的跑不起來呢……先說SignInt沒有,改成Signed之後說
the trait `std::path::BytesContainer` is not implemented for the type `&collections::string::String`
什麼的……

順便儘量按照Python版本逐行翻譯了個C++14的:
https://github.com/farseerfc/swapview/blob/master/C%2B%2B14/swapview.cpp
(不知道是否足夠C++14,感覺用的還都是老特性……)

laike9m 说:
Dec 25, 2014 10:47:40 AM

话说我见到很多人都很看好 Rust 呢,我也打算学一学

Avatar_small
依云 说:
Dec 25, 2014 11:04:15 AM

请从 thestinger 源安装 rust-git。官方里是 0.12 版本。在到达 1.0 版本之前各个版本间的兼容性一直会很差的(我昨天更新了 rust-git,然后收到了三个 deprecated warning 呢)。

Avatar_small
依云 说:
Dec 25, 2014 11:59:36 AM

merge 了喵~稍微改了一下。
我这里测试的结果是,比 Rust 版略慢。

Avatar_small
fc 说:
Dec 25, 2014 12:28:03 PM

我rust也編譯成功啦。

是呢,我這邊也比Rust略慢一點。估計是因爲vector複製了之類的原因?有時間再試試手動優化一下。

菜鸟浮出水 说:
Dec 25, 2014 06:03:34 PM

发了个lua5.1的pull request 0 - 0

Avatar_small
依云 说:
Dec 25, 2014 06:30:08 PM

求修到能够与已有版本对比的程度 0.0

Avatar_small
依云 说:
Dec 25, 2014 09:37:48 PM

我已经给修好了喵……

Jex 说:
Dec 29, 2014 10:34:18 PM

我照抄Python用Haskell写了一模一样的一个,发现速度真的比Python慢很多倍,感掉好丢人啊。
BTW,没有自动类型提升,fromIntegral之类的方法多么操蛋啊。

https://gist.github.com/JexCheng/f523e6ec288889dd7471

Avatar_small
依云 说:
Dec 29, 2014 10:52:18 PM

呃,你的没有做错误处理,是拿 sudo 跑的么……

你不会是拿 runhaskell 跑的吧。编译时加 -O2,我写的版本耗时是 Python 3 的两倍,而另一个优化过的版本和 Python 3 的已经很接近了。

Jex 说:
Dec 30, 2014 10:56:49 AM

知道,故意不加错误处理的,也没用 printf,限定了类型,也O2了。速度比你的第一版本还慢。你的第二个版本我试了user运行时间大约是Python的两倍。

Jex 说:
Dec 30, 2014 11:07:43 AM

是root 跑的。

以前听说即使搞程序证明的都用OCaml,现在有点明白为啥了。为了写出个性能表现正常些的Haskell程序要绕这么多的路 TOT 。

Rust将printf作为宏的想法,我不久前也在考虑,将format字符串在编译期解释,既可以获得类型安全,又可以获得性能提升,加上Constant Folding,我估计Rust够聪明的话,未来它应该只要求format string参数是可在编译期确定即可,好像听说C++新版的constexpr也在向这方面发展,不过估计他们得撸个C++/Rust的解释器内嵌到编译器中才行

Jex 说:
Dec 30, 2014 11:15:12 AM

我预想的PL可以这样:

```
class A {
"forEach orderBy map reduce sort".words.forEach \methodName->{
self[methodName]= Enumerable.method methodName
}
}

```

上面的代码可在编译期expanding,不知道有没有哪个语言已经抢先实现了,Taiji?但性能表现不行就不如直接用Ruby了

Avatar_small
依云 说:
Dec 30, 2014 11:19:25 AM

我的 OCaml 版本更慢呢……

Rust 那样是快,可是翻译怎么办呢?

Jex 说:
Dec 30, 2014 11:33:58 AM

可以让"Hello:%(name)s,Id:%d" 变成
["Hello:","Id:"] `zipGensym` [name,itoa(i)]
zipGensym 是动态创建的一个函数,其类型根据formats 推导出来。

对应gettext文件编译格式也要转变。这样getext格式就不通用了,但总要有技术革新的嘛。(反正我也早受够那个叫什么po格式的文件了,还不如直接用JSON映射方便,修改一次还要重新编译

如果OCaml也慢的话,printf可能需要运行期的类型检查,regexp我不清楚具体实现,除了这些,我就不知道该说什么好了,TOT

Avatar_small
依云 说:
Dec 30, 2014 12:43:37 PM

JSON 不支持注释,逗号的使用反人类,到处是引号也很烦。

po 我觉得还好。编译什么的,反正整个项目都是要编译的。

TOML 看上去不错的样子。

Avatar_small
依云 说:
Dec 30, 2014 12:45:08 PM

Lisp 宏就可以做到类似的效果啊。

Jex 说:
Dec 30, 2014 01:38:09 PM

宏和这个不一样,试试更复杂的例子:

```
object=new A
B.methods.forEach \(methodName,method) -> {
object[methodName]=method
}
map print B.methods
```
(Class)B.methods是可在编译期确定的,假设类型为`[Method::B this -> Any]`,但除非B.methods.forEach、map本身是一个宏,否则做不到这点。

(卫生)宏摆脱不了这个缺陷,它跟语言的对象系统是分开的,普通函数和宏必须加以区别,这正是问题所在。比如String有regex match方法,如果regex是constexpr, string也是constexpr,那么结果就也可以是constexpr。再如,我要将Regex /a\d+/编译成DFA,我希望这个编译是在编译期完成,我觉得用宏实现不太实际,如果macro与普通函数不作区别,那么就轻松多了,只需要在编译期将所有constexpr执行一遍,相当于编译期的JIT(呃,可能Lisp的一些实现使用dump内存的编译方式或能达到类似效果。

具体细节以后等我实现好了再解释,反正我是不给(即使卫生)macro留地方站的

Jex 说:
Dec 30, 2014 03:42:29 PM

照抄Python的Racket实现:

https://gist.github.com/JexCheng/165c14dea694f731b2f8

比Haskell稍微快那么一点点点

Avatar_small
依云 说:
Dec 30, 2014 04:35:56 PM

没有错误处理啊,而且输出格式也不一样。

Jex 说:
Dec 30, 2014 04:41:45 PM

是float precision么?它就那样的。

Avatar_small
依云 说:
Dec 30, 2014 06:27:51 PM

不过指定格式的么?

Jex 说:
Dec 30, 2014 08:27:50 PM

它的format 方法precision参数原来要设成 '(= 1) 才会始终保留".0"

更改过了的:
https://gist.github.com/JexCheng/165c14dea694f731b2f8

coolwanglu 说:
Jan 03, 2015 10:24:49 PM

有很多string copy, 不科学!
试试用一些std::move

Avatar_small
fc 说:
Jan 04, 2015 03:13:13 AM

雖然標準裏沒有作強制要求,但是basic_string<char>和vector<char>的重要區別之一就是basic_string允許實現爲Copy-on-Write的。據我所知實際上gcc和VC和stlport的實現也的確是CoW的並且附帶了小串內嵌的優化。
TL;DR string的copy的開銷(不修改內容的話)和move語義差不多了啦,即使在98標準化前的C++也是這樣,這不像vector。

Avatar_small
fc 说:
Jan 04, 2015 03:16:02 AM

雖然標準裏沒有作強制要求,但是basic_string<char>和vector<char>的重要區別之一就是basic_string允許實現爲Copy-on-Write的。據我所知實際上gcc和VC和stlport的實現也的確是CoW的並且附帶了小串內嵌的優化。
TL;DR string的copy的開銷(不修改內容的話)和move語義差不多了啦,即使在98標準化前的C++也是這樣,這不像vector。
不過歡迎改進和PR,自動benchmark基本準備好了,用std::move是否可以快一些跑一下就知道啦。


登录 *


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

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