gpt4 book ai didi

c++ - 提高 mmap memcpy 文件读取性能

转载 作者:IT老高 更新时间:2023-10-28 23:21:50 33 4
gpt4 key购买 nike

我有一个从文件中顺序读取数据的应用程序。有些是直接从指向 mmaped 文件的指针读取的,而其他部分是从文件 memcpyed 到另一个缓冲区的。我注意到在我需要的所有内存(1MB block )中执行较大的 memcpy 时性能很差,而在执行大量较小的 memcpy 调用时性能更好(在我的测试中,我使用了 4KB 的页面大小,运行了 1/3 的时间。)我认为问题是使用大型 memcpy 时出现大量重大页面错误。

我尝试了各种调整参数(MAP_POPUATEMADV_WILLNEEDMADV_SEQUENTIAL),但没有任何明显改善。

我不确定为什么许多小的 memcpy 调用应该更快;这似乎违反直觉。有什么办法可以改善吗?

结果和测试代码如下。

在 CentOS 7 (linux 3.10.0) 上运行,默认编译器 (gcc 4.8.5),从常规磁盘的 RAID 阵列读取 29GB 文件。

使用 /usr/bin/time -v 运行:

4KB memcpy:

User time (seconds): 5.43
System time (seconds): 10.18
Percent of CPU this job got: 75%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:20.59
Major (requiring I/O) page faults: 4607
Minor (reclaiming a frame) page faults: 7603470
Voluntary context switches: 61840
Involuntary context switches: 59

1MB memcpy:

User time (seconds): 6.75
System time (seconds): 8.39
Percent of CPU this job got: 23%
Elapsed (wall clock) time (h:mm:ss or m:ss): 1:03.71
Major (requiring I/O) page faults: 302965
Minor (reclaiming a frame) page faults: 7305366
Voluntary context switches: 302975
Involuntary context switches: 96

MADV_WILLNEED 似乎对 1MB 的复制结果影响不大。

MADV_SEQUENTIAL 将 1MB 的复制结果拖慢了很多,我没等它完成(至少 7 分钟)。

MAP_POPULATE 将 1MB 的复制结果减慢了大约 15 秒。

用于测试的简化代码:

#include <algorithm>
#include <iostream>
#include <stdexcept>

#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
try {
char *filename = argv[1];

int fd = open(filename, O_RDONLY);
if (fd == -1) {
throw std::runtime_error("Failed open()");
}

off_t file_length = lseek(fd, 0, SEEK_END);
if (file_length == (off_t)-1) {
throw std::runtime_error("Failed lseek()");
}

int mmap_flags = MAP_PRIVATE;
#ifdef WITH_MAP_POPULATE
mmap_flags |= MAP_POPULATE; // Small performance degredation if enabled
#endif

void *map = mmap(NULL, file_length, PROT_READ, mmap_flags, fd, 0);
if (map == MAP_FAILED) {
throw std::runtime_error("Failed mmap()");
}

#ifdef WITH_MADV_WILLNEED
madvise(map, file_length, MADV_WILLNEED); // No difference in performance if enabled
#endif

#ifdef WITH_MADV_SEQUENTIAL
madvise(map, file_length, MADV_SEQUENTIAL); // Massive performance degredation if enabled
#endif

const uint8_t *file_map_i = static_cast<const uint8_t *>(map);
const uint8_t *file_map_end = file_map_i + file_length;

size_t memcpy_size = MEMCPY_SIZE;

uint8_t *buffer = new uint8_t[memcpy_size];

while (file_map_i != file_map_end) {
size_t this_memcpy_size = std::min(memcpy_size, static_cast<std::size_t>(file_map_end - file_map_i));
memcpy(buffer, file_map_i, this_memcpy_size);
file_map_i += this_memcpy_size;
}
}
catch (const std::exception &e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}

return 0;
}

最佳答案

如果底层文件和磁盘系统不够快,无论你使用 mmap() 还是 POSIX open()/read() 或标准 C fopen()/fread() 或 C++ iostream 根本不重要。

如果性能真的很重要并且底层文件和磁盘系统足够快,那么 mmap() 可能是顺序读取文件的最糟糕的方法。映射页面的创建是一项相对昂贵的操作,并且由于每个数据字节只能读取一次,因此每次实际访问的成本可能非常高。使用 mmap() 还会增加系统的内存压力。您可以在阅读页面后显式 munmap() 页面,但是当映射被拆除时,您的处理可能会停止。

使用直接 IO 可能是最快的,尤其是对于大文件,因为不涉及大量页面错误。 Direct IO 绕过了页面缓存,这对于只读取一次的数据来说是件好事。缓存数据只读取一次 - 永远不会被重新读取 - 不仅没有用,而且可能会适得其反,因为 CPU 周期习惯于从页面缓存中驱逐有用的数据。

示例(为清楚起见,省略了标题和错误检查):

int main( int argc, char **argv )
{
// vary this to find optimal size
// (must be a multiple of page size)
size_t copy_size = 1024UL * 1024UL;

// get a page-aligned buffer
char *buffer;
::posix_memalign( &buffer, ( size_t ) ( 4UL * 1024UL ), copy_size );

// make sure the entire buffer's virtual-to-physical mappings
// are actually done (can actually matter with large buffers and
// extremely fast IO systems)
::memset( buffer, 0, copy_size );

fd = ::open( argv[ 1 ], O_RDONLY | O_DIRECT );

for ( ;; )
{
ssize_t bytes_read = ::read( fd, buffer, copy_size );
if ( bytes_read <= 0 )
{
break;
}
}

return( 0 );
}

在 Linux 上使用直接 IO 时存在一些注意事项。文件系统支持可能参差不齐,直接 IO 的实现可能很挑剔。您可能必须使用页面对齐的缓冲区来读取数据,如果文件的最后一页不是整页,您可能无法读取它。

关于c++ - 提高 mmap memcpy 文件读取性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52845387/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com