0

如果我需要将一个大文件从分配的内存写入磁盘,最有效的方法是什么?

目前,我使用以下内容:

char* data = static_cast<char*>(operator new(0xF00000000)); // 60 GB 

// Do something to fill `data` with data

std::ofstream("output.raw", std::ios::binary).
   write(data, 0xF00000000);

但我不确定最直接的方法是否也是最有效的,考虑到各种缓冲机制等。

我正在使用带有 64 位目标的 Windows 7 64 位和 Visual Studio 2012 RC 编译器。

4

4 回答 4

2

对于 Windows,您应该使用CreateFileAPI。仔细阅读该页面以及其中提到优化的任何链接。您传入一些标志来关闭缓冲。过去,当我以大约每秒 800MB 的速度收集视频时,我会这样做,并且必须尽快将其中的一小部分写入 RAID 阵列。

现在,对于标志 - 我认为主要是这些:

  • FILE_FLAG_NO_BUFFERING
  • FILE_FLAG_WRITE_THROUGH

对于阅读,您可能想要使用FILE_FLAG_SEQUENTIAL_SCAN,尽管我认为如果关闭缓冲,这将无效。

查看缓存行为部分

你需要做几件事。首先,您应该始终写入扇区大小的倍数的数据量。这几乎是(或至少是)512 字节,但您将来可能要考虑最多 2048 个字节。

其次,您的内存也必须与该扇区大小对齐。您可以使用_aligned_malloc()或仅分配比您需要的更多的缓冲区并手动对齐。

可能还有其他内存优化问题,您可能希望将单个写入操作限制为内存页面大小。我从来没有进入过那个深度。我仍然能够以非常接近磁盘极限的速度写入数据。它比使用 stdio 调用要快得多。

如果您需要在后台执行此操作,您可以使用重叠 I/O,但老实说我从未理解它。我创建了一个后台工作线程,专门用于写出视频缓冲区并在外部对其进行控制。

于 2012-08-20T22:42:42.823 回答
1

想到的最有希望的事情是内存映射输出文件。根据数据的填充方式,您甚至可以让现有程序通过指针直接写入磁盘,最后不需要单独的写入步骤。这相信操作系统可以有效地分页文件,无论如何它可能与堆内存有关......可能会避免磁盘到磁盘的复制。

我不确定如何专门在 Windows 中执行此操作,但您可能可以通知操作系统您的预期内存访问模式以进一步提高性能。

(boost::asio 具有对内存映射文件的可移植支持)

于 2012-08-20T22:44:49.173 回答
1

如果你想使用std::ofstream,你应该确保以下几点:

  1. 文件流不使用缓冲区。这样做的方法是调用out.setbuf(0, 0).
  2. 确保std::localeused by stream 不进行任何字符转换,即std::use_facet<std::codecvt<char, char> >(loc).always_noconv()yield true。语言环境执行此"C"操作。

有了这个,我希望这std::ofstream与写入大缓冲区的任何其他方法一样快。我还希望它比使用内存映射 I/O 慢,因为内存映射 I/O 在读取它们只是为了写入它们的内容时应该避免对内存的分页部分。

于 2012-08-20T23:12:21.613 回答
1

使用 来打开一个文件CreateFile,使用SetEndOfFile为文件预分配空间(以避免写入时产生太多碎片),然后WriteFile在循环中使用 2 MB 大小的缓冲区(此大小在大多数情况下效果最佳)调用,直到您写入整个文件出去。

FILE_FLAG_NO_BUFFERING在某些情况下可能会有所帮助,而在其他情况下可能会使情况变得更糟,因此没有必要使用它,因为通常 Windows 文件系统写入缓存可以很好地工作。

于 2012-08-20T23:53:47.463 回答