为什么带有 MAP_POPULATE 的匿名大页 mmap 需要这么多时间?

问题描述

我想评估page fault的开销,所以我设置了实验:mmap匿名64GB内存,然后随机走2MB大页面。对于页面遍历,我 random_shuffle 遍历顺序,并写入每个页面的第一个字节。为了研究页面错误的成本,我在使用和不使用 MAP_POPULATE 的情况下执行 mmap。

但是,我发现带有 MAP_POPULATE 的 mmap 花费了不合理的长时间。结果如下

using 2MB huge page

with populate:
mmap takes 27 s
page walk takes 9.5 ms

without populate:
mmap takes 31 us
page walk takes 146 ms

MAP_POPULATE 的 mmap 的成本是 27 秒,比不带 MAP_POPULATE 的 mmap 加上翻页时间长几个数量级。我知道 MAP_POPULATE 会为映射预先设置页表,但 27s 太长而不合理。

更新:

我关闭了大页面并使用 4KB 常规页面代替。结果现在是合理的:

using 4KB pages

with populate:
mmap: 27s
walk: 4.8s
total: ~32s

without populate:
mmap: 13 us
walk: 32s
total: ~32s

那么,为什么使用大页面会导致异常结果?

代码:

代码很短,所以我在下面列出。

#include <stdio.h>
#include <iostream>
#include <sys/mman.h>
#include <algorithm>
#include <random>
#include <chrono>

// MAP_HUGE_2MB is not defined in my system. define it.
#ifndef MAP_HUGE_2MB
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
#endif

int main()
{
    bool populate = true;

    int flag = MAP_ANONYMOUS | MAP_SHARED | MAP_HUGE_2MB;
    flag |= populate ? MAP_POPULATE : 0;
    size_t size = 64ull * 1024 * 1024 * 1024;
    size_t page_size = 2 * 1024 * 1024;
    auto now = std::chrono::steady_clock::now();
    void *m = mmap(NULL,size,PROT_READ | PROT_WRITE,flag,-1,0);
    if (m == MAP_FAILED)
    {
        fprintf(stderr,"failed to mmap: errno: %d\n",errno);
        return 0;
    }

    auto then = std::chrono::steady_clock::now();
    auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(then - now)
                  .count();
    std::cout << "mmap time(ns): " << ns << ",populate: " << populate << std::endl;

    // randomized page walk
    size_t times = size / page_size;
    std::vector<int> walk_order;
    // generate the randomized walk order
    walk_order.reserve(times);
    for (size_t t = 0; t < times; ++t)
    {
        walk_order.push_back(t);
    }
    std::random_shuffle(walk_order.begin(),walk_order.end());

    now = std::chrono::steady_clock::now();
    // walk the page
    for (auto t: walk_order)
    {
        char *target = (char *) m + t * page_size;
        *target = 'a';
    }
    then = std::chrono::steady_clock::now();
    ns = std::chrono::duration_cast<std::chrono::nanoseconds>(then - now)
             .count();

    fprintf(stderr,"walk ns: %ld\n",ns);
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...