C 通过延迟/性能测量确定NUMA布局
最近,我一直在观察我无法解释的内存密集型工作负载对性能的影响。为了弄清这一点,我开始运行几个微基准点,以确定常见的性能参数,如缓存线大小和一级/二级/三级缓存大小(我已经知道了,我只是想看看我的测量是否反映了实际值) 对于缓存线测试,我的代码大致如下(Linux C,但其概念与Windows等类似): 当我开始将C 通过延迟/性能测量确定NUMA布局,c,performance,caching,prefetch,numa,C,Performance,Caching,Prefetch,Numa,最近,我一直在观察我无法解释的内存密集型工作负载对性能的影响。为了弄清这一点,我开始运行几个微基准点,以确定常见的性能参数,如缓存线大小和一级/二级/三级缓存大小(我已经知道了,我只是想看看我的测量是否反映了实际值) 对于缓存线测试,我的代码大致如下(Linux C,但其概念与Windows等类似): 当我开始将数组大小增加到大于L3大小的值时,我得到了大量不可靠的数字,即使大量重复也无法平衡。我无法通过这种方式确定可用于性能评估的模式,更不用说确定NUMA条带的起始、结束或位置了 然后,我发现
数组大小增加到大于L3大小的值时,我得到了大量不可靠的数字,即使大量重复也无法平衡。我无法通过这种方式确定可用于性能评估的模式,更不用说确定NUMA条带的起始、结束或位置了
然后,我发现硬件预取器足够智能,可以识别我的简单访问模式,并在访问之前将所需的行提取到缓存中。在数组索引中添加一个随机数会增加每个元素的时间,但在其他方面似乎没有多大帮助,可能是因为每次迭代我都有一个rand()
调用。对我来说,预先计算一些随机值并将它们存储在一个数组中似乎不是一个好主意,因为这个数组也会存储在一个热缓存中,从而扭曲我的测量结果。将STEP
增加到4097或8193也没有多大帮助,预取程序一定比我聪明
我的方法是否明智/可行,或者我是否错过了大局?有可能观察到这样的NUMA潜伏期吗?如果是,我做错了什么?
我禁用了地址空间随机化只是为了确保并排除奇怪的缓存锯齿效应。在测量之前,是否还有其他操作系统需要调整
有可能观察到这样的NUMA潜伏期吗?如果是,我做错了什么
内存分配器是NUMA感知的,因此在默认情况下,除非明确要求在另一个节点上分配内存,否则不会观察到任何NUMA效果。实现此效果最简单的方法是numactl(8)。只需在一个节点上运行应用程序,并将内存分配绑定到另一个节点,如下所示:
numactl --cpunodebind 0 --membind 1 ./my-benchmark
另见numa_alloc_onnode(3)
在测量之前,是否还有其他操作系统需要调整
关闭CPU缩放功能,否则您的测量可能会有噪音:
find '/sys/devices/system/cpu/' -name 'scaling_governor' | while read F; do
echo "==> ${F}"
echo "performance" | sudo tee "${F}" > /dev/null
done
现在关于测试本身。当然,要测量延迟,访问模式必须是(伪)随机的。否则,您的测量将被快速缓存命中所污染
以下是您如何实现这一目标的示例:
数据初始化
用随机数填充数组:
static void random_data_init()
{
for (size_t i = 0; i < ARR_SZ; i++) {
arr[i] = rand();
}
}
32K/64K(我的一级缓存~32K)、256K/512K(我的二级缓存~256K)和6144K/8192K(我的三级缓存~6M)之间有明显的步骤。Re:预取器,英特尔预取器执行黑魔法。我听说它在你当前的步幅上应用了多重多项式拟合来神奇地预取,等等。你最好的办法可能是使用MTRRs来标记一个不可缓存的内存范围,然后在这个范围内进行基准测试。啊,我想用一个单独的随机数组来分散预取程序的注意力,但我认为它本身很热会污染我的缓存。使用随机值初始化数组本身并从本质上执行指针跟踪是一个好主意。我会试试的,谢谢。至于NUMA控制-我实际上在一个本身不是NUMA的系统上,但仍然表现出这样的特性(malloc不知道NUMA),因此我不能依赖numactl。当数组足够大时,即使不绑定它,是否也可以从不同的节点强制内存?@SonnyO'Rullivan如果您的系统不是NUMA,那么这实际上取决于这些页面如何交错。您可能会从NUMA0获得一个页面,然后从NUMA1获得几个页面。那么这样的方法真的会给你带来噪音。您可以尝试另一种方法,用相同的数据污染缓存,一次只交换一页。但是,可能很难发现如此微妙的变化。。。有一件事是肯定的:您应该将测试与页面大小对齐…@andriyberestovsky-我可以得到您编写的基准测试代码吗?这对我有很大帮助。
find '/sys/devices/system/cpu/' -name 'scaling_governor' | while read F; do
echo "==> ${F}"
echo "performance" | sudo tee "${F}" > /dev/null
done
static void random_data_init()
{
for (size_t i = 0; i < ARR_SZ; i++) {
arr[i] = rand();
}
}
const size_t OPERATIONS = 1 * 1000 * 1000; // 1M operations per iteration
int random_step_sizeK(size_t size)
{
size_t idx = 0;
for (size_t i = 0; i < OPERATIONS; i++) {
arr[idx & (size - 1)]++;
idx += arr[idx & (size - 1)] * 64; // assuming cache line is 64B
}
return 0;
}
----------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------
random_step_sizeK/4 4217004 ns 4216880 ns 166
random_step_sizeK/8 4146458 ns 4146227 ns 168
random_step_sizeK/16 4188168 ns 4187700 ns 168
random_step_sizeK/32 4180545 ns 4179946 ns 163
random_step_sizeK/64 5420788 ns 5420140 ns 129
random_step_sizeK/128 6187776 ns 6187337 ns 112
random_step_sizeK/256 7856840 ns 7856549 ns 89
random_step_sizeK/512 11311684 ns 11311258 ns 57
random_step_sizeK/1024 13634351 ns 13633856 ns 51
random_step_sizeK/2048 16922005 ns 16921141 ns 48
random_step_sizeK/4096 15263547 ns 15260469 ns 41
random_step_sizeK/6144 15262491 ns 15260913 ns 46
random_step_sizeK/8192 45484456 ns 45482016 ns 23
random_step_sizeK/16384 54070435 ns 54064053 ns 14
random_step_sizeK/32768 59277722 ns 59273523 ns 11
random_step_sizeK/65536 63676848 ns 63674236 ns 10
random_step_sizeK/131072 66383037 ns 66380687 ns 11