std::vector 保留和调整 NUMA 局部性

Posted

技术标签:

【中文标题】std::vector 保留和调整 NUMA 局部性【英文标题】:std::vector reserve & resize NUMA locality 【发布时间】:2020-08-26 21:11:25 【问题描述】:

我目前正在研究优化我的应用程序的 NUMA 位置。

到目前为止,我认为我理解内存将驻留在分配后首先接触它的那个 NUMA 节点。

关于 std::vector(使用默认分配器)的问题是:

std::vector::reserve 分配新内存 - 但它是否也触及它?如果没有,我如何在调用保留后强制触摸它? std::vector::resize 是否触及内存? 采用 size_t 的构造函数呢?

关于 NUMA 的总体情况:

如果已经被触动的内存被分页到磁盘,然后再次被访问并产生一个硬故障,这算作新的第一次触动还是加载到内存中的页面驻留在触动它的 numa 节点最初是?

我正在使用 c++11 线程。只要我在一个线程中并分配/接触新内存,我是否可以确定所有这些内存都将驻留在同一个 numa 节点上,或者操作系统是否有可能在我的线程下切换执行 CPU 而它执行然后我的一些分配将在一个 NUMA 域中,而其他分配在另一个域中?

【问题讨论】:

【参考方案1】:

假设我们谈论的是 Intel CPU:在他们的 Nahlem 老式 CPU 上,如果您有两个这样的 CPU,则有一个开机选项可以告诉它们如何在它们之间划分物理内存。物理架构是通过 QPI 连接的两个 CPU,每个 CPU 控制自己的一组内存 SIMM。选项是,

    一个 CPU 上物理地址空间的前半部分,下一个 CPU 上的后半部分,或者

    CPU 之间的内存页交替

对于第一个选项,如果您分配了一块内存,它将由操作系统从物理地址空间中获取,然后我想一个好的调度程序会努力运行访问该内存的线程在控制它的 CPU 上。对于第二个选项,如果您分配了几页内存,那么它将在两个物理 CPU 之间拆分,然后调度程序对访问它的线程做了什么并不重要。我实际上只是简单地玩了一下,并没有真正发现其中的区别。英特尔在 QPI 方面做得很好。我对较新的 Intel 架构不太熟悉,但我认为它大致相同。

另一个问题真的是 NUMA 节点是什么意思?如果我们指的是现代 Intel 和 AMD CPU,它们为软件提供了一个合成的 SMP 环境,使用诸如 QPI / Hypertransport(现在是它们的现代等效物)之类的东西在 NUMA 硬件架构之上执行此操作。因此,在谈论 NUMA 局部性时,实际上是操作系统调度程序是否会在控制线程正在访问的 RAM 的 CPU 上的核心上运行线程(SMP 意味着它可以在任何核心上运行并且仍然访问,尽管可能有轻微的延迟差异,内存,无论它在物理内存中分配的位置)。我不知道答案,但我认为有些人会这样做。当然,我为使用线程和内存的核心亲和性所做的努力与仅仅让操作系统(Linux 2.6)做这件事相比只产生了微小的改进。现代 CPU 上的缓存系统以及它们与 QPI 等 CPU 间互连的交互非常聪明。

可追溯到 SMP 真正是纯硬件时的旧操作系统 SMP 不会知道这样做。

小兔子洞 - 如果我们指的是纯 NUMA 系统(Transputers,PS3 之外的 Cell 处理器及其 SPE),那么线程将在特定内核上运行,并且只能访问该内核的内存;要访问(由另一个线程)在另一个内核的内存中分配的数据,软件必须通过在某些互连上发送数据来自行对其进行排序。在学习之前,这很难编码,但结果可能非常快。英特尔花了大约 10 年的时间才达到原始数学速度的 Cell 处理器。

【讨论】:

以上是关于std::vector 保留和调整 NUMA 局部性的主要内容,如果未能解决你的问题,请参考以下文章

std::vector 调整大小算法

C++ 将预先保留的哈希映射(std::unordered_map)与整数键和连续数据数组(std::vector)进行比较

std::vector 向下调整大小

C++ STD 容器:std::vector - 有没有办法清除向量并保留存储分配?

保留后使用 std::vector::data

如何在不丢失现有数据的情况下调整 std::vector 的大小?