我需要以行方式处理大量的中型到大型文件(几百MB到GB),所以我对用于迭代线的标准D方法感兴趣.这个foreach(line; file.byLine())成语似乎符合要求,并且令人愉快,简洁易读,但性能似乎不太理想.
例如,下面是Python和D中的两个简单程序,用于迭代文件行并计算行数.对于~470 MB的文件(~3.6M行),我得到以下时间(最好的10个):
D次:
real 0m19.146s
user 0m18.932s
sys 0m0.190s
Run Code Online (Sandbox Code Playgroud)
Python时间(在EDIT 2之后,见下文):
real 0m0.924s
user 0m0.792s
sys 0m0.129s
Run Code Online (Sandbox Code Playgroud)
这是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;
}
Run Code Online (Sandbox Code Playgroud)
现在相应的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
Run Code Online (Sandbox Code Playgroud)
编辑2:我改变了Python代码,使用了for line in infile下面评论中建议的更加惯用,从而为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;
}
Run Code Online (Sandbox Code Playgroud)
最后一个版本的时间如下:
real 0m3.201s
user 0m2.820s
sys 0m0.376s
Run Code Online (Sandbox Code Playgroud)
And*_*scu 13
编辑和TL; DR:此问题已在https://github.com/D-Programming-Language/phobos/pull/3089中解决.File.byLine从D 2.068开始,将提供改进的性能.
我在一个包含575247行的文本文件上尝试了您的代码.Python基线大约需要0.125秒.这是我的代码库,每个方法的注释中都嵌入了时序.解释如下.
import std.algorithm, std.file, std.stdio, std.string;
int main(string[] args)
{
if (args.length < 2) {
return 1;
}
size_t linect = 0;
// 0.62 s
foreach (line; File(args[1]).byLine())
linect += 1;
// 0.2 s
//linect = args[1].readText.count!(c => c == '\n');
// 0.095 s
//linect = args[1].readText.representation.count!(c => c == '\n');
// 0.11 s
//linect = File(args[1]).byChunk(4096).joiner.count!(c => c == '\n');
writeln("There are: ", linect, " lines.");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我用于dmd -O -release -inline每个变种.
第一个版本(最慢)一次读取一行.我们可以而且应该提高byLine的性能; 目前它被诸如byLine与其他C stdio操作的混合使用所困扰,这可能过于保守.如果我们废除它,我们可以轻松地进行预取等.
第二个版本一下子读取文件,然后使用标准算法用谓词计算行.
第三个版本承认没有必要考虑任何UTF细微之处; 计数字节也一样好,因此它将字符串转换为字节方式(免费),然后计算字节数.
最后一个版本(我最喜欢的)一次从文件中读取4KB的数据并使用它来懒散地展平它们joiner.然后它再次计算字节数.