微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为什么带有 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 举报,一经查实,本站将立刻删除。