我如何做 stdin.byLine,但有一个缓冲区?
How do I do stdin.byLine, but with a buffer?
我正在读取数 GB 的文件并从 stdin 处理它们。我正在像这样从 stdin 读取。
string line;
foreach(line1; stdin.byLine){
line = to!string(line1);
...
}
有更快的方法吗?我尝试了
的线程方法
auto childTid = spawn(&fn, thisTid);
string line;
foreach(line1; stdin.byLine){
line = to!string(line1);
receiveOnly!(int);
send(childTid, line);
}
int x= 0;
send(childTid, x);
这允许它在我的进程 运行 以复制操作为代价时从磁盘加载至少一行,但这仍然很愚蠢,我需要的是 fgets,或者一种方法来结合 stdio.byChunk(4096) 与 readline。我试过 fgets。
char[] buf = new char[4096];
fgets(buf.ptr, 4096, stdio)
但它总是失败,因为 stdio 是文件而不是流。不确定如何使其成为流。如果您采用您认为最好的方法,我们将不胜感激。我不太擅长D,对于任何菜鸟错误表示歉意。
引擎盖下实际上已经有两层缓冲(不包括硬件本身):C 运行时库和内核都做了一层缓冲以最小化 I/O 成本。
首先,内核将磁盘中的数据保存在自己的缓冲区中,并且会向前看,如果您遵循可预测的模式,则加载超出您在单个调用中请求的内容。这是为了减轻与查找设备相关的低级成本,并将跨进程缓存 - 如果您使用一个程序读取文件然后再次使用第二个程序,第二个程序可能会从内核内存缓存而不是物理磁盘中获取它并且可能明显更快。
其次,构建 D 的 std.stdio 的 C 库也保留了一个缓冲区。 readln
最终调用 C 文件 I/O 一次从内核读取一个块的函数。 (有趣的是,写入也由 C 库缓冲,如果用户交互则默认为行缓冲,否则为块缓冲。写入速度很慢,按块做会有很大的不同,但有时 C 库认为管道不是交互式的当它出现并导致常见问题解答时:Simple D program Output order is wrong )
这些 C 库缓冲区还通过在发送到内核之前对其进行批处理来减轻许多小读写的成本。在 readln 的情况下,它可能一次读取几千字节,即使你只请求一行或一个字节,其余的留在缓冲区中以备下次使用。
所以你的 readln 循环已经自动缓冲并且应该获得不错的 I/O 性能。
不过,您可以通过一些技巧自己做得更好。在这种情况下,您可以尝试对内存映射文件使用 std.mmfile
并像 i 是一个数组一样读取它,但是您的文件太大而无法容纳在 32 位上。虽然可能在 64 位上工作。 (请注意,内存映射文件不会一次加载,它只是映射到一个内存地址。当您实际触摸它的一部分时,操作系统将按需 load/save。)
或者,当然,您可以使用较低级别的操作系统函数,例如 import core.sys.posix.unistd
中的 write
或 import core.sys.windows.windows
中的 WriteFile
,这将绕过 C 库的层(但是,当然,保留你想要的内核层,不要试图绕过它们。)
如果您想了解更多关于使用这些函数的信息,您可以查找任何 win32 或 posix 系统调用 C 教程。它在 D 中与在 C 中相同,但有一些小注意事项,例如导入而不是 #include
。
加载块后,您将希望扫描它以查找换行符并很可能将其切片以形成传递给循环或其他算法的范围。 std.range
和 std.algorithm
模块也有可能有用的搜索、拆分和分块功能,但您需要小心跨越缓冲区边缘的行,以保持正确性和效率。
但是如果你的性能足够好,我会说就这样吧——C lib+kernel 的缓冲在大多数情况下都做得很好。
我正在读取数 GB 的文件并从 stdin 处理它们。我正在像这样从 stdin 读取。
string line;
foreach(line1; stdin.byLine){
line = to!string(line1);
...
}
有更快的方法吗?我尝试了
的线程方法 auto childTid = spawn(&fn, thisTid);
string line;
foreach(line1; stdin.byLine){
line = to!string(line1);
receiveOnly!(int);
send(childTid, line);
}
int x= 0;
send(childTid, x);
这允许它在我的进程 运行 以复制操作为代价时从磁盘加载至少一行,但这仍然很愚蠢,我需要的是 fgets,或者一种方法来结合 stdio.byChunk(4096) 与 readline。我试过 fgets。
char[] buf = new char[4096];
fgets(buf.ptr, 4096, stdio)
但它总是失败,因为 stdio 是文件而不是流。不确定如何使其成为流。如果您采用您认为最好的方法,我们将不胜感激。我不太擅长D,对于任何菜鸟错误表示歉意。
引擎盖下实际上已经有两层缓冲(不包括硬件本身):C 运行时库和内核都做了一层缓冲以最小化 I/O 成本。
首先,内核将磁盘中的数据保存在自己的缓冲区中,并且会向前看,如果您遵循可预测的模式,则加载超出您在单个调用中请求的内容。这是为了减轻与查找设备相关的低级成本,并将跨进程缓存 - 如果您使用一个程序读取文件然后再次使用第二个程序,第二个程序可能会从内核内存缓存而不是物理磁盘中获取它并且可能明显更快。
其次,构建 D 的 std.stdio 的 C 库也保留了一个缓冲区。 readln
最终调用 C 文件 I/O 一次从内核读取一个块的函数。 (有趣的是,写入也由 C 库缓冲,如果用户交互则默认为行缓冲,否则为块缓冲。写入速度很慢,按块做会有很大的不同,但有时 C 库认为管道不是交互式的当它出现并导致常见问题解答时:Simple D program Output order is wrong )
这些 C 库缓冲区还通过在发送到内核之前对其进行批处理来减轻许多小读写的成本。在 readln 的情况下,它可能一次读取几千字节,即使你只请求一行或一个字节,其余的留在缓冲区中以备下次使用。
所以你的 readln 循环已经自动缓冲并且应该获得不错的 I/O 性能。
不过,您可以通过一些技巧自己做得更好。在这种情况下,您可以尝试对内存映射文件使用 std.mmfile
并像 i 是一个数组一样读取它,但是您的文件太大而无法容纳在 32 位上。虽然可能在 64 位上工作。 (请注意,内存映射文件不会一次加载,它只是映射到一个内存地址。当您实际触摸它的一部分时,操作系统将按需 load/save。)
或者,当然,您可以使用较低级别的操作系统函数,例如 import core.sys.posix.unistd
中的 write
或 import core.sys.windows.windows
中的 WriteFile
,这将绕过 C 库的层(但是,当然,保留你想要的内核层,不要试图绕过它们。)
如果您想了解更多关于使用这些函数的信息,您可以查找任何 win32 或 posix 系统调用 C 教程。它在 D 中与在 C 中相同,但有一些小注意事项,例如导入而不是 #include
。
加载块后,您将希望扫描它以查找换行符并很可能将其切片以形成传递给循环或其他算法的范围。 std.range
和 std.algorithm
模块也有可能有用的搜索、拆分和分块功能,但您需要小心跨越缓冲区边缘的行,以保持正确性和效率。
但是如果你的性能足够好,我会说就这样吧——C lib+kernel 的缓冲在大多数情况下都做得很好。