5

我正在一台有 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. 当另一个 NUMA 节点的核心首先接触内存时,由 NUMA施加的内存映射限制mbind()仅保持 50%。我希望这被记录在某个地方,因为悄悄地违背承诺并不好......

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

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

4

2 回答 2

3

正如您在阅读中已经发现的那样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

可能 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 次迭代前后失败,并且所有分配都正确绑定。

于 2013-10-31T09:49:30.540 回答
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.
于 2013-10-29T21:34:55.053 回答