Performance 我如何确定我的拍子代码运行如此缓慢的原因?

Performance 我如何确定我的拍子代码运行如此缓慢的原因?,performance,racket,Performance,Racket,为了好玩,我编写了一个快速的Racket命令行脚本来解析旧的Unix fortune文件。《财富》文件只是一个巨大的文本文件,在空白行上有一个分隔条目的% 作为第一次快速破解,我编写了以下代码: (define fortunes (with-input-from-file "fortunes.txt" (λ () (regexp-split #rx"%" (port->string))))) 我想它几乎会立刻运行。相反,它需要很长时间才能运行几分钟。相

为了好玩,我编写了一个快速的Racket命令行脚本来解析旧的Unix fortune文件。《财富》文件只是一个巨大的文本文件,在空白行上有一个分隔条目的
%

作为第一次快速破解,我编写了以下代码:

(define fortunes
    (with-input-from-file "fortunes.txt"
      (λ ()
        (regexp-split #rx"%" (port->string)))))
我想它几乎会立刻运行。相反,它需要很长时间才能运行几分钟。相比之下,我所认为的等效Python:

with open('fortunes.txt') as f:
    fortunes = f.read().split('%')
立即执行,结果与球拍代码相同

我做错了什么?是的,有一些明显的低挂果实,比如我确信如果我不使用
port->string
,将整个文件拖入RAM,事情会更好,但是这种行为是如此病态地糟糕,我觉得我必须在更高的层次上做一些愚蠢的事情

有没有一种更像球拍的方式来达到同样的效果?对于某些操作,球拍I/O是否真的很差?有没有什么方法可以比DrRacket中天真的探查器更深入地剖析我的代码,这样我就可以知道给定的行会导致什么问题

编辑:我使用的《财富》文件是FreeBSD的,位于,重约2MB。OS X Lion上Racket 5.1.3 x64的最佳运行时间为:

real    1m1.479s
user    0m57.400s
sys     0m0.691s
对于Python 2.7.1 x64,它是:

real    0m0.057s
user    0m0.029s
sys     0m0.015s

Eli说的没错,时间几乎完全花在了
regexp split
(虽然在
port->string
中花费了整整一秒钟),但我不清楚是否有一种更可取但同样简单的方法。

看起来大部分成本都是由于在字符串上运行
regexp split
。我发现的最快的替代方法是拆分字节字符串,然后将结果转换为字符串:

(map bytes->string/utf-8
     (call-with-input-file "db"
       (λ (i) (regexp-split #rx#"%" (port->bytes i)))))
使用~2MB的随机fortune DB,您的代码大约需要35秒,而此版本需要33毫秒

(我还不知道为什么一根绳子要花这么长时间,但它肯定太慢了。)


编辑:我们跟踪到了一个效率缺陷。粗略描述:当Racket对字符串进行
regexp匹配时,它会将字符串的大部分转换为字节字符串(UTF-8)进行搜索。此函数是在C中实现的核心函数。
regexp split
反复使用它来查找所有匹配项,因此不断重新执行此转换。我正在寻找一种更好的方法,但在修复之前,请使用上述解决方法。

这在最新Git HEAD版本的Racket中已修复,请参阅:。您的示例现在对我来说只需0.1秒。

我刚刚用
“/usr/share/games/fortunes/work”
(105 KB)而不是
“fortunes.txt”
,尝试了您的代码,不到十分之一秒。“fortunes.txt”
有多大?你在哪个平台上运行?它是FreeBSD的~2MB fortunes文件;你可以在上抓到它。也许用Racket提交一份bug报告?我还不清楚它本身是个bug。例如,Squeak的纯Smalltalk正则表达式库的性能只是稍好一些,但是
String
上有一个split方法,比我上面给出的Python更快。我只是看不到任何像
字符串拆分
序列拆分
这样的功能可能会更快,但我完全同意这一点,因为我找不到文档,这就是我问的原因。本杰明:我们跟踪到了一个效率缺陷。粗略描述:当Racket对字符串进行
regexp匹配时,它会将字符串的大部分转换为字节字符串(UTF-8)进行搜索。此函数是在C中实现的核心函数。
regexp split
反复使用它来查找所有匹配项,因此不断重新执行此转换。我正在寻找一种更好的方法,但在修复之前,请使用上述解决方法。这在最新Git-HEAD版本的Racket中得到了修复,请参阅:,现在我可以在0.1秒内运行您的示例。