c - mmap 对于不同的文件大小表现不同

标签 c file mmap

我正在使用 mmap 来读取文件。现在,当文件大小约为 880 MB 时,迭代文件大约需要 0.5 秒。

现在我通过复制文件内容将文件大小增加了 10 倍。现在再次使用 mmap 大约需要 1 分钟。

我认为迭代时间应该随着文件大小线性增加。

这是简单的测试代码

FILE *fp = fopen64("filename", "rm");
if (fp == NULL)
{
    perror(NULL);
}
struct stat st;
fstat(fileno(fp), &st);
off_t fileSize = st.st_size;
char *data = (char *)mmap64(NULL, fileSize, PROT_READ,MAP_PRIVATE,fileno(fp), 0);
off_t ptr =0;
char c =(char)0;
while(ptr < fileSize) { c += data[ptr++] ;}
std::cout << c << "\n";

这是结果。

文件大小为 880MB

real    0m0.501s
user    0m0.447s
sys     0m0.053s

文件大小为 8.8 GB

real    0m57.685s
user    0m10.773s
sys     0m3.690s

最佳答案

访问时间不是恒定的,数据集越大,访问时间就越慢。我建议阅读Latency Numbers Every Programmer Should Know .

如果您运行基准测试并调整数据集大小,您将看到性能变化的几个范围。

  1. 当数据集适合 L1 缓存时,性能最快。 L1 缓存很小,例如每个核心 64 KiB,但速度很快(约 1 个周期的访问时间,几乎与寄存器一样快)。

  2. 当您需要二级缓存时,性能会突然下降。 L2 缓存比 L1 缓存更大且速度更慢。性能下降了大约 10 倍左右。

  3. 当数据集对于二级缓存来说太大但又适合 RAM 时,性能会再次下降。由于缓存未命中,性能又下降了 10 倍左右。

  4. 当数据集对于 RAM 来说太大但又适合磁盘时,性能会急剧下降。假设您有一个快速 SSD,则缓存未命中的性能损失约为 1000 倍,如果您有非 SSD 硬盘驱动器,性能损失可能为 100,000 倍。

您的 880 MB 数据集完全适合 8 GiB RAM,但 8,800 MB 数据集则不然,它不能同时驻留在其中。随机访问模式有些悲观,但即使使用线性访问模式,您的页面也会从缓存中逐出,并且内核将不得不一遍又一遍地从磁盘读取它们。

假装您拥有无限量的存储空间且速度完全相同,这很好,但事实远非如此。

红鲱鱼

  • 实际上,将文件加载到内存中的唯一两种方法是使用 readmmap 。其他选项只是这两个选项之上的一层。对于顺序访问不在页面缓存中的数据,read 之间的区别和mmap不相关,请参阅mmap() vs. reading blocks

  • 访问模式会改变数据集变大时性能下降的程度,但不会改变数据集太大而无法驻留的事实,无法比磁盘更快。

    <

小注释

  • 如果您要mmap然后使用open不是fopenfopen没有必要。

  • "m" fopen 的标志没有做你认为它做的事,它在这里没有任何作用。

  • 不要使用open64 , fopen64 , mmap64或任何废话。只需使用 #define _FILE_OFFSET_BITS 64 。这是现代的处理方式,但当然,它仅适用于 32 位系统 - 并且因为您使用的是 mmap在偏移量为零时,没有任何意义。

  • 调用 perror但继续下去就是一个错误。 err()该功能并非普遍可用,但可以满足您的需求。

  • 没有充分的理由不使用MAP_SHARED在这里,但这不会改变任何东西。

以下是具有更一致的错误检查的代码的外观:

int fp = open("filename", O_RDONLY);
if (fp == -1)
    err(1, "open");
struct stat st;
int r = fstat(fp, &st);
if (r == -1)
    err(1, "stat");
// Compiler warning on 64-bit, but is correct
if (st.st_size > (size_t)-1)
    errx(1, "file too large");
size_t sz = st.st_size;
void *data = mmap(NULL, sz, PROT_READ, MAP_SHARED, fp, 0);
if (data == MAP_FAILED)
    err(1, "mmap");
unsigned counter = 0;
for (char *ptr = data, end = ptr + sz; ptr != end; ptr++)
    counter += *ptr;
printf("%u\n", counter);

关于c - mmap 对于不同的文件大小表现不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40272437/

相关文章:

c - 将从 readdir() 返回的字符串放入 char **

c - 函数指针 : physical or virtual address

python - 将 4 个 numpy 数组中的坐标写入文件 Python

Java OutOfMemory 异常 : mmap error on loading zip file

c - 学习 C 并遇到错误(我认为主要是语法,但我正在学习!)

java - C和Java之间的内存分配

c - C中的文件创建权限

java - 如何在java中创建输出文件?

c - 使用 mremap() 将两个相同的页面合并为一个物理页面

c - c中的mmap函数可接受的数据的最大大小是多少?