由于您只是简单地流式传输数据并且从未重新读取它,因此页面缓存对您没有任何好处。实际上,考虑到您通过页面缓存推送的数据量和应用程序的内存压力,否则有用的数据可能会从页面缓存中驱逐出去,并且您的系统性能会因此受到影响。
因此,在读取数据时不要使用缓存。使用直接IO。根据
Linux open()
man page:
O_DIRECT (since Linux 2.4.10)
Try to minimize cache effects of the I/O to and from this
file. In general this will degrade performance, but it is
useful in special situations, such as when applications do
their own caching. File I/O is done directly to/from user-
space buffers. The O_DIRECT
flag on its own makes an effort
to transfer data synchronously, but does not give the
guarantees of the O_SYNC flag that data and necessary metadata
are transferred. To guarantee synchronous I/O, O_SYNC
must be
used in addition to O_DIRECT
. See NOTES below for further
discussion.
...
NOTES
...
O_DIRECT
The O_DIRECT flag may impose alignment restrictions on the length and
address of user-space buffers and the file offset of I/Os. In Linux
alignment restrictions vary by filesystem and kernel version and
might be absent entirely. However there is currently no
filesystem-independent interface for an application to discover these
restrictions for a given file or filesystem. Some filesystems
provide their own interfaces for doing so, for example the
XFS_IOC_DIOINFO operation in xfsctl(3).
Under Linux 2.4, transfer sizes, and the alignment of the user buffer
and the file offset must all be multiples of the logical block size
of the filesystem. Since Linux 2.6.0, alignment to the logical block
size of the underlying storage (typically 512 bytes) suffices. The
logical block size can be determined using the ioctl(2) BLKSSZGET
operation or from the shell using the command:
blockdev --getss
...
由于您不会反复读取数据,直接IO很可能会稍微提高性能,因为数据将直接从磁盘传输到应用程序的内存中,而不是从磁盘传输到页面缓存,然后再传输到应用程序的内存中。
使用低级别的C风格I/O,使用open()/read()/close(),并使用O_DIRECT标志打开文件。
int fd = ::open( filename, O_RDONLY | O_DIRECT )
这将导致数据直接读入应用程序的内存,而不会缓存在系统的页面缓存中。
您需要使用对齐的内存进行read()操作,因此您需要类似于以下内容才能实际读取数据:
char *buffer;
size_t pageSize = sysconf( _SC_PAGESIZE );
size_t bufferSize = 32UL * pageSize;
int rc = ::posix_memalign( ( void ** ) &buffer, pageSize, bufferSize );
posix_memalign()
是一个符合POSIX标准的函数,它返回一个按要求对齐的内存指针。页面对齐缓冲区通常已经足够了,但是将其对齐到巨大页面大小(在x86-64上为2MiB)将提示内核你想要透明巨大页面来分配内存,当你以后读取它时,访问缓冲区更有效率。
ssize_t bytesRead = ::read( fd, buffer, bufferSize );
没有您的代码,我无法确定如何将数据从
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
测量时间。然后查看页面缓存中的数据量。
接着执行以下操作:
echo 3 | sudo tee /proc/sys/vm/drop_caches
dd if=/your/big/data/file iflag=direct of=/dev/null bs=32k
在此之后,检查页面缓存中的数据量...
您可以尝试不同的块大小,以查看在您的硬件和文件系统上哪种效果最好。
请注意,直接IO非常依赖于实现。执行直接IO的要求在不同的文件系统之间可能会有很大差异,并且性能可能会因IO模式和特定硬件而发生显著变化。 大多数情况下,这些依赖关系都不值得,但是通常情况下,唯一简单的用途是在不重新读取/重写任何部分数据的情况下流式传输大型文件。
mmap
调用中使用了MAP_POPULATE
标志,则所有映射的数据都将被预取到内存中。 - user7860670