Ben*_*ack 6 performance racket
为了好玩,我写了一个快速的Racket命令行脚本来解析旧的Unix fortune文件.财富文件只是一个巨大的文本文件,%一个空白行分隔条目.
就像第一次快速入侵一样,我写了下面的Racket代码:
(define fortunes
(with-input-from-file "fortunes.txt"
(? ()
(regexp-split #rx"%" (port->string)))))
Run Code Online (Sandbox Code Playgroud)
我以为它会立即运行.相反,它需要很长时间才能运行几分钟的顺序.相比之下,我认为是等效的Python:
with open('fortunes.txt') as f:
fortunes = f.read().split('%')
Run Code Online (Sandbox Code Playgroud)
立即执行,与Racket代码具有相同的结果.
我在这做错了什么?是的,有一些显而易见的低调成果,比如我确信如果我不把整个文件淹没到内存中,情况会更好port->string,但是这种行为在病态上是不好的我觉得好像我必须做某事愚蠢到比这更高的水平.
是否有类似Racket的方式来实现这一目标,同样具有更好的性能?某些操作的Racket I/O真的很差吗?有没有一些方法来分析我的代码比DrRacket天真探查略深,所以我可以找出关于给定的行导致了问题?
编辑:我使用的命运文件是在http://fortunes.cat-v.org/freebsd/上找到的FreeBSD ,其重量约为2 MB.OS X Lion上Racket 5.1.3 x64的最佳运行时间是:
real 1m1.479s
user 0m57.400s
sys 0m0.691s
Run Code Online (Sandbox Code Playgroud)
对于Python 2.7.1 x64,它是:
real 0m0.057s
user 0m0.029s
sys 0m0.015s
Run Code Online (Sandbox Code Playgroud)
Eli是对的,几乎完全花费了时间regexp-split(虽然似乎花费了一整秒port->string),但我不清楚有一个首选但同样简单的方法.
看起来大部分成本是由于regexp-split在字符串上运行.我找到的最快的替代方法是拆分字节字符串,然后将结果转换为字符串:
(map bytes->string/utf-8
(call-with-input-file "db"
(? (i) (regexp-split #rx#"%" (port->bytes i)))))
Run Code Online (Sandbox Code Playgroud)
使用大约2MB的随机算命DB,你的代码需要大约35秒,而这个版本需要33毫秒.
(我不确定为什么它在字符串上需要这么长时间,但它肯定太慢了.)
编辑:我们跟踪它是一个效率错误.粗略描述:当Racket regexp-match对字符串执行操作时,它会将字符串的大部分转换为字节字符串(UTF-8)以进行搜索.此函数是在C中实现的核心函数,regexp-split它反复使用它来查找所有匹配项,因此不断重新执行此转换.我正在寻找一种更好的方法,但在修复之前,请使用上述解决方法.