在Ubuntu(Linux OS)中将一个巨大的文件读入C++向量

Ren*_*hen 9 c++ linux ubuntu caching file

在我在Linux(Ubuntu 14.4)中运行的C++程序中,我需要读取一个完全缓存在C++向量中的90 GB文件,而我只有125 GB的内存.

当我按块读取文件块时,它会不断导致linux中缓存的mem使用率增加,结果是128 GB内存的50%以上,然后可用内存容易变为50 GB以下.

              total        used        free      shared  buff/cache   available
Mem:            125          60         0           0          65         65
Run Code Online (Sandbox Code Playgroud)

Swap: 255 0 255

所以我发现可用内存然后变为零,文件读取过程几乎停止了,我必须手动运行:

echo 3 | sudo tee /proc/sys/vm/drop_caches
Run Code Online (Sandbox Code Playgroud)

清除缓存的mem,以便恢复文件读取过程.我理解缓存的mem是为了加快再次读取文件.我的问题是如何避免手动运行drop cache命令以确保文件读取过程能够成功完成?

And*_*nle 3

由于您只是流式传输数据并且从不重新读取它,因此页面缓存对您没有任何好处。事实上,考虑到您通过页面缓存推送的数据量以及应用程序的内存压力,否则有用的数据可能会从页面缓存中逐出,并且您的系统性能会因此受到影响。

所以读取数据时不要使用缓存。使用直接IO。根据Linuxopen()手册页

O_DIRECT(自 Linux 2.4.10 起)

尝试最小化进出该文件的 I/O 的缓存影响。一般来说,这会降低性能,但在特殊情况下很有用,例如当应用程序进行自己的缓存时。文件 I/O 直接在用户空间缓冲区之间进行。该O_DIRECT标志本身努力同步传输数据,但不保证 O_SYNC 标志传输数据和必要的元数据。为了保证同步 I/O,O_SYNC除了 之外还必须使用O_DIRECT. 进一步讨论请参阅下面的注释。

...

笔记

...

O_DIRECT

O_DIRECT 标志可以对用户空间缓冲区的长度和地址以及 I/O 的文件偏移施加对齐限制。在 Linux 中,对齐限制因文件系统和内核版本而异,并且可能完全不存在。然而,当前没有独立于文件系统的接口供应用程序发现给定文件或文件系统的这些限制。一些文件系统为此提供了自己的接口,例如 xfsctl(3) 中的 XFS_IOC_DIOINFO 操作。

在Linux 2.4下,传输大小以及用户缓冲区的对齐和文件偏移量都必须是文件系统逻辑块大小的倍数。从 Linux 2.6.0 开始,与底层存储的逻辑块大小(通常为 512 字节)对齐就足够了。可以使用 ioctl(2) BLKSSZGET 操作或从 shell 使用以下命令来确定逻辑块大小:

      blockdev --getss
Run Code Online (Sandbox Code Playgroud)

...

由于您不会一遍又一遍地读取数据,因此直接 IO 可能会在一定程度上提高性能,因为数据将直接从磁盘进入应用程序的内存,而不是从磁盘进入页面缓存,然后进入应用程序的内存。

open()通过/ read()/使用低级 C 风格 I/O close(),并使用以下标志打开文件O_DIRECT

int fd = ::open( filename, O_RDONLY | O_DIRECT );
Run Code Online (Sandbox Code Playgroud)

这将导致数据直接读入应用程序的内存,而不缓存在系统的页面缓存中。

您必须read()使用对齐的内存,因此您需要这样的东西来实际读取数据:

char *buffer;
size_t pageSize = sysconf( _SC_PAGESIZE );
size_t bufferSize = 32UL * pageSize;

int rc = ::posix_memalign( ( void ** ) &buffer, pageSize, bufferSize );
Run Code Online (Sandbox Code Playgroud)

posix_memalign()是一个 POSIX 标准函数,它返回一个指向按请求对齐的内存的指针。页对齐的缓冲区通常已经足够了,但是对齐大页大小(x86-64 上为 2MiB)将提示内核您需要透明的大页来进行该分配,从而在稍后读取缓冲区时更有效地访问它。

ssize_t bytesRead = ::read( fd, buffer, bufferSize );
Run Code Online (Sandbox Code Playgroud)

没有您的代码,我无法说出如何从buffer您的中获取数据std::vector,但这应该不难。有可能的方法可以使用某种类型的 C++ 流包装 C 风格的低级文件描述符,并配置该流以使用与直接 IO 正确对齐的内存。

如果您想查看差异,请尝试以下操作:

echo 3 | sudo tee /proc/sys/vm/drop_caches
dd if=/your/big/data/file of=/dev/null bs=32k
Run Code Online (Sandbox Code Playgroud)

到时候了。然后查看页面缓存中的数据量。

然后执行以下操作:

echo 3 | sudo tee /proc/sys/vm/drop_caches
dd if=/your/big/data/file iflag=direct of=/dev/null bs=32k
Run Code Online (Sandbox Code Playgroud)

之后检查页面缓存中的数据量...

您可以尝试不同的块大小,看看哪种最适合您的硬件和文件系统。

但请注意,直接 IO非常依赖于实现。不同文件系统之间执行直接 IO 的要求可能会有很大差异,并且性能可能会根据您的 IO 模式和特定硬件而有很大差异。 大多数时候,这些依赖项并不值得,但通常值得的一个简单用途是流式传输一个巨大的文件,而无需重新读取/重写数据的任何部分。