快速跨平台的C++文件读写算法

5
我想提出一个看似简单的问题,但我无法在任何地方找到答案。 是否有一种快速的现代算法,可用于文件输入和/或输出,并可与所有符合标准的C ++编译器编译,并在所有操作系统上工作,而无需外部库的要求?
  1. 我发现最快的方法是使用内存映射文件,但这样做不行,因为我们希望同一段代码在所有平台上工作
  2. 我们不能使用像Win32 API这样的API,因为那将使其特定于平台
  3. 我不想使用c,我希望算法只是纯粹的c++代码,如果可行,可以使用stl,而不是一些丑陋的c,其中夹杂着汇编hack / trick
  4. 不应该使用不属于标准C ++的框架或外部库,如wxWidgets,Qt,MFC等。
  5. 整个问题的重点是算法尽可能地快,类似于使用内存映射文件的速度,更快会更好,但我知道这是不可能的

除了我之外,您是否见过其他人研究过如此疯狂的东西? 这样的算法是否可能存在?

感谢任何建议

7个回答

9
在以下限制条件下: “可以使用所有符合标准的C++编译器进行编译,并且适用于所有操作系统,无需外部库要求吗?” 你基本上限制了自己只能使用标准库文件IO函数。也许是POSIX函数(取决于您考虑哪个子集的“所有符合标准的C++编译器”)。 如果它们不够快,则必须开始放弃某些限制。

1
同意。唯一的C++标准文件I/O是来自标准库的,即头文件<iostream>或<cstdio>。如果你想要快速的文件I/O,则最重要的事情是不要一个字节一个字节地读写文件,而是一次读/写大块。 - stakx - no longer contributing
整个文件可以使用iostream一次性读取吗?这样做会提供性能优势吗?我认为不会。难道cstdio不是C头文件吗? - user258883
2
一次性读取整个文件仍然比将其映射到内存中慢。 - Anon.
stdio.h 是 C 语言的头文件,而 cstdio 则是 C++ 中声明相同功能的头文件。在追求极致速度时忽略它,我认为是不明智的。但这是你的事情。 - dmckee --- ex-moderator kitten

9

这与“算法”无关。

当涉及到将数据写入文件时,你需要依赖操作系统 - 内存映射文件之所以“快”,是因为你只需写入内存,而操作系统会在自己的时间内同步它。如果操作系统不支持它,那就没办法了 - 除非你想要实现自己的内存映射层。

顺便提一下,POSIX有 mmap,所以如果你限制自己只在符合POSIX标准的系统上运行,那么没问题。


4

换个角度看“操作系统的恩惠”,文件复制过程中的大部分开销都在于操作系统。碎片化的文件读取需要比整理好的文件更多的时间。C++没有通用或标准的函数可用于检测碎片化的文件。

C++中最快的方法:

std::ifstream in_file;
std::ofstream out_file;

out_file << in_file.rdbuf();

您可以使用关键字“copy file rdbuf”在网上查找更多详细信息。上面的片段将复制工作留给操作系统,但在所有平台上都可移植。通过读取C ++ i / o流,您可以设置读取缓冲区的大小或使用自己的缓冲区。

更快的文件复制需要特定于平台的功能,例如DMA传输。使用线程和多个缓冲区可以加速此过程;但是C ++不支持线程(有一个事实上的标准POSIX支持线程)。一个线程将读入缓冲区,而另一个线程从缓冲区中写入。


2

一些要点:

  • 这与算法无关。
  • 想要面向所有操作系统不是一个非常有成效的目标(而且是不可能实现的)。在你测试之前,你的代码不能在特定平台上工作。相反,我会专注于一些可行的操作系统——比如POSIX + Win32。
  • 在这种情况下,你可以进行内存映射,例如通过为Windows实现mmap()(在MapViewOfFile()等之上——如果需要灵感,git源代码中有一个针对Windows的mmap实现)。
  • 如果你不能使用内存映射,则建议使用普通的C文件API,而不是C++的文件流,如果性能很重要。尽管C++的流对于某些操作具有更高的潜力,但实际上它要慢得多。
  • 但是,为了获得良好的性能,通常“足够好”的方法就是确保以合理的方式处理数据。按顺序读取数据,不要重新读取等。完美是好的敌人;)

@kusma: 我很想看看C的stdio和C++的iostream功能之间的性能比较。你有没有可能指导我去这样的资源?因为我曾经查看过C++ iostream库源代码(更具体地说,是随MinGW一起提供的实现),并且印象中它实际上只是一个非常薄的(基于模板)包装器,围绕C文件I/O函数而建立的。因此,我不希望看到任何显著的性能差异。 - stakx - no longer contributing
上帝的敌人?哎呀... :) - Drew Hall
stakx:很遗憾,不行。结果早已丢失。我在2001年左右对MSVC进行了一些分析,因为一位朋友报告说使用std::istream(与C API相比)会导致GCC / Linux明显减速。如果我没记错的话,我们都发现性能下降大约相同,约为30%。不过,也许我的记忆有误。Drew:抱歉,我是指“撒旦的敌人”,当然是这个意思;) - kusma

1

按文件系统块大小的倍数(或2的幂)顺序读取块可能会有所帮助。然后在块加载到内存后拆分数据。某个地方有一篇白皮书,他们测试了各种块大小的性能。我希望我能再次找到它。

您还可以尝试为从文件中读取块和在内存中执行数据操作(当然要使用适当的同步)分别设置专用线程。这允许您在阻塞文件读取调用时使用CPU处理数据。

无论如何,如果您尝试这些想法,请告诉我们是否注意到了差异。来自基准测试的实际结果将是有趣的。


大多数硬盘使用512字节的倍数块大小。对于更大的硬盘,可能是1024或更大。 - Thomas Matthews
上层/下层使用的缓冲区大小可能与文件系统不同。尝试使用不同的2的幂次方(不要限制在512/1024,一直增加到256kiB)。我记得在Linux中的某个层中使用了4kiB这个神奇数字,但我不记得是在哪里了。抱歉我不能更具体。 - Emile Cormier
4k是文件系统块的默认大小。 - Anon.

1

快速IO通常可以归结为两个方面:

  1. 最小化数据拷贝
  2. 最小化内核/用户上下文切换

大多数IO技术都试图解决其中的一个问题。我所知道的跨平台IO的最快速度的代码是Perl IO系统。建议查看源代码。Perl黑客已经花费数十年时间,在尽可能多的平台上尽可能地提高他们的IO速度。


0
其他帖子的回答是正确的,因为性能总是与通用性(跨平台)相互矛盾。
但是,一般来说,通过“缓冲”输入来读取相对较大的数据块,并处理这些数据块,可以获得最佳结果。
我知道这是一个非常基本和普遍的答案,但这就是你在不更具体地针对特定平台或不了解正在处理的具体输入的情况下所能得到的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接