【问题标题】:Are there limits on allocating small chunks using numa_alloc_onnode()?使用 numa_alloc_onnode() 分配小块是否有限制?
【发布时间】:2013-11-08 12:51:12
【问题描述】:

我正在一台有 4 个 Operton 6272 处理器、运行 centOS 的机器上试验 NUMA。有 8 个 NUMA 节点,每个节点有 16GB 内存。

这是我正在运行的一个小测试程序。

void pin_to_core(size_t core)
{
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core, &cpuset);
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}

int main()
{
    pin_to_core( 0 );

    size_t bufSize = 100;
    for( int i = 0; i < 131000; ++i )
    {
        if( !(i % 10) )
        {
            std::cout << i << std::endl;
            long long free = 0;
            for( unsigned j = 0; j < 8; ++j )
            {
                numa_node_size64( j, &free );
                std::cout << "Free on node " << j << ": " << free << std::endl;
            }
        }

        char* buf = (char*)numa_alloc_onnode( bufSize, 5 );
        for( unsigned j = 0; j < bufSize; ++j )
            buf[j] = j;
    }

    return 0;
}

所以基本上运行在核心 #0 上的线程在 NUMA 节点 5 上分配 131K 100 字节缓冲区,用垃圾初始化它们并泄漏它们。每 10 次迭代,我们打印出有关每个 NUMA 节点上可用内存量的信息。

在输出的开头我得到:

0
Free on node 0: 16115879936
Free on node 1: 16667398144
Free on node 2: 16730402816
Free on node 3: 16529108992
Free on node 4: 16624508928
Free on node 5: 16361529344
Free on node 6: 16747118592
Free on node 7: 16631336960
...

最后我得到:

Free on node 0: 15826657280
Free on node 1: 16667123712
Free on node 2: 16731033600
Free on node 3: 16529358848
Free on node 4: 16624885760
Free on node 5: 16093630464
Free on node 6: 16747384832
Free on node 7: 16631332864
130970
Free on node 0: 15826657280
Free on node 1: 16667123712
Free on node 2: 16731033600
Free on node 3: 16529358848
Free on node 4: 16624885760
Free on node 5: 16093630464
Free on node 6: 16747384832
Free on node 7: 16631332864
mbind: Cannot allocate memory
mbind: Cannot allocate memory
mbind: Cannot allocate memory
mbind: Cannot allocate memory
mbind: Cannot allocate memory
mbind: Cannot allocate memory
mbind: Cannot allocate memory
130980
...

我不清楚的事情:

1) 为什么会有那些“mbind:无法分配内存”消息?事实上,如果我将缓冲区大小更改为 1000,我远未用完所有内存并且行为不会改变,这让我认为我的某种内核资源句柄已经用完了.

2) 尽管我要求在节点 5 上分配内存,但实际分配似乎已在节点 0 和 5 之间进行分配。

谁能提供任何关于为什么会发生这种情况的见解?

更新

希望就第 (2) 点提供更多详细信息。一些内存未在节点 5 上分配的事实似乎与我们正在初始化核心 #0(属于 NUMA 节点 0)上的缓冲区这一事实有关。如果我将 pin_to_core(0) 更改为 pin_to_core(8),则分配的内存将在节点 1 和 5 之间拆分。如果是 pin_to_core(40),则所有内存都分配在节点 5 上。

更新2

我查看了 libnuma 的源代码并尝试用来自那里的更多低级调用替换对 numa_alloc_onnode() 的调用:mmap()mbind()。我现在还要检查内存驻留在哪个 NUMA 节点上——我为此使用了move_pages() 调用。结果如下。在初始化之前(j 上的循环)页面没有映射到任何节点(我得到 ENOENT 错误代码)并且在初始化之后页面被分配给节点 0 或节点 5。模式是常规的:5,0, 5,0,... 和以前一样,当我们接近第 131000 次迭代时,对 mbind() 的调用开始返回错误代码,并且当这种情况发生时,页面总是分配给节点 0。 mbind 返回的错误代码是 ENOMEM,文档说这意味着“内核内存”用完了。我不知道它是什么,但它不可能是“物理”内存,因为我每个节点有 16GB。

到目前为止,这是我的结论:

  1. mbind() 对内存映射施加的限制仅在另一个 NUMA 节点的核心首先接触内存时保持 50% 的时间。我希望这被记录在某个地方,因为悄悄地违背承诺并不好......

  2. mbind 的调用次数有限制。所以应该尽可能mbind()大内存块。

我要尝试的方法是:在固定到特定 NUMA ndo 内核的线程上执行内存分配任务。为了让您更加安心,我会尝试调用 mlock(因为here 描述的问题)。

【问题讨论】:

    标签: c++ linux memory-management numa


    【解决方案1】:

    正如您在阅读 libnuma.c 时已经发现的那样,每次调用 numa_alloc_onnode() 都会创建一个新的匿名内存映射,然后将内存区域绑定到指定的 NUMA 节点。如此多次调用mmap(),您只是达到了每个进程允许的最大内存映射数。该值可以从/proc/sys/vm/max_map_count 读取,也可以由系统管理员通过写入伪文件来修改:

    # echo 1048576 > /proc/sys/vm/max_map_count
    

    sysctl:

    # sysctl -w vm.max_map_count=1048576
    

    5 月 Linux 发行版上的默认值是 65530 映射。 mmap() 实现映射合并,即它首先尝试在创建新映射之前扩展现有映射。在我的测试中,它会在每一秒调用中创建一个新映射,否则会扩展前一个映射。在第一次调用 numa_alloc_onnode() 之前,我的测试进程有 37 个映射。因此mmap() 应该在2 * (65530-37) = 130986 调用之后的某个地方开始失败。

    看起来当mbind() 应用于现有映射的一部分时,会发生一些奇怪的事情,并且新受影响的区域没有正确绑定。我必须深入研究内核源代码才能找出原因。另一方面,如果你替换:

    numa_alloc_onnode( bufSize, 5 )
    

    numa_alloc_onnode( bufSize, i % 4 )
    

    不执行任何映射合并,mmap() 在第 65500 次迭代前后失败,所有分配均已正确绑定。

    【讨论】:

    • 谢谢!我很确定你是对的,我正在达到 max_map_count。奇怪的是,该文件的内容在我的系统上是 65530... 你知道为什么我在一个节点上获得内存而不是我要求的节点吗?
    【解决方案2】:

    对于您的第一个问题,来自numa_alloc_onnode 的手册页

    The size argument will be rounded up to a multiple of the system page size.
    

    这意味着尽管您请求的数据量很小,但您却获得了整个页面。也就是说,在您的程序中,您实际上是在请求 131000 个系统页面。

    对于您的第二个问题,如果无法在给定节点上分配页面,我建议使用numa_set_strict() 强制numa_alloc_onnode 失败。

    numa_set_strict() sets a flag that says whether the functions  allocating
       on specific nodes should use use a strict policy. Strict means the
       allocation will fail if the memory cannot be allocated  on  the  target
       node.   Default operation is to fall back to other nodes.  This doesn't
       apply to interleave and default.
    

    【讨论】:

    • 感谢您的回复。我确实意识到每次分配都会花费​​一个内存页面。不过,这只是 4KB * 131000 =~ 520MB。所以我用完了内存以外的东西——我猜是一些系统资源。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 1970-01-01
    • 2022-10-23
    • 2015-11-03
    • 1970-01-01
    相关资源
    最近更新 更多