【发布时间】:2015-05-09 10:44:15
【问题描述】:
我需要以逐行方式处理大量中型到大型文件(几百 MB 到 GB),因此我对用于迭代行的标准 D 方法感兴趣。 foreach(line; file.byLine()) 成语似乎符合要求,简洁易读,但性能似乎不太理想。
例如,下面是 Python 和 D 中的两个简单程序,用于迭代文件的行并计算行数。对于约 470 MB 文件(约 3.6M 行),我得到以下时间(最好是 10 次):
D 次:
real 0m19.146s
user 0m18.932s
sys 0m0.190s
Python 时代(EDIT 2 之后,见下文):
real 0m0.924s
user 0m0.792s
sys 0m0.129s
这里是D版,用dmd -O -release -inline -m64编译:
import std.stdio;
import std.string;
int main(string[] args)
{
if (args.length < 2) {
return 1;
}
auto infile = File(args[1]);
uint linect = 0;
foreach (line; infile.byLine())
linect += 1;
writeln("There are: ", linect, " lines.");
return 0;
}
现在是对应的 Python 版本:
import sys
if __name__ == "__main__":
if (len(sys.argv) < 2):
sys.exit()
infile = open(sys.argv[1])
linect = 0
for line in infile:
linect += 1
print "There are %d lines" % linect
编辑 2:我更改了 Python 代码以使用更惯用的 for line in infile,如下面的 cmets 中所建议的那样,从而为 Python 版本带来了更大的加速,现在即将推出标准 wc -l 调用 Unix wc 工具的速度。
关于我在 D 中可能做错的任何建议或指示,导致性能如此糟糕?
编辑:为了比较,这里有一个 D 版本,它将 byLine() 成语抛出窗口并立即将所有数据吸入内存,然后将数据分成几行 post-hoc .这提供了更好的性能,但仍然比 Python 版本慢约 2 倍。
import std.stdio;
import std.string;
import std.file;
int main(string[] args)
{
if (args.length < 2) {
return 1;
}
auto c = cast(string) read(args[1]);
auto l = splitLines(c);
writeln("There are ", l.length, " lines.");
return 0;
}
最后一个版本的时间安排如下:
real 0m3.201s
user 0m2.820s
sys 0m0.376s
【问题讨论】:
-
尝试了不同版本的 dmd (2.067.0-b3, 2.066.1, 2.064.2),结果大致相同。罪魁祸首似乎是
-m64。在本地,对于由短行(不超过 100 个字符)组成的 200M 文件,32 位版本的运行速度比 Python 快一点(1.5 对 1.8 秒),但 64 位版本需要 6.9 秒,这比 32 位差 4 倍以上。也许某种 64 位代码生成效率低下,值得在 issues.dlang.org 上报告为错误。 -
附带说明,另一个优化标志是“-noboundscheck”(或自 2.066 起支持的替代形式“-boundscheck=off”)。它完全禁用数组边界检查。也就是说,在这种情况下它并没有多大帮助。
-
当我在没有“-m64”标志的情况下进行编译时,性能会稍差(尽管我使用的是 64 位机器,OS X 10.10;dmd v2.066)
-
使用
-m32标志失败并出现ld: symbol(s) not found for architecture i386错误。我已经在 dlang.org 网站上打开了一个问题,包括指向我用于测试目的的文件的链接。见issues.dlang.org/show_bug.cgi?id=14256。感谢您的帮助。 -
readlines将所有内容读入内存;list(file)是一种更惯用的方法,但在这种情况下,您应该只使用for line in infile。请注意,如果您只想比较纯 IO 速度,则应考虑使用更快的可迭代计数方法 like given here - CPython 不是快速解释器。