Apple M1处理器上的内存访问

2021-01-09 15:43:50

当程序大多只是随机访问内存时,标准成本模型将计算独立随机访问的次数。一般的想法是,内存访问比大多数其他计算任务要慢得多。

此外,成本模型可以扩展为将“附近”的内存访问计数为免费。也就是说,如果我在内存地址x读取一个字节,然后在内存地址x + 1读取一个字节,则可以假定第二个字节是“免费的”。

这种幼稚的内存访问模型通常很明智。但是,您应始终牢记,这仅仅是一个模型。模型可能无法预测实际性能。

它怎么会失败?一个CPU内核可以一次发出多个内存请求。因此,如果我需要一次访问7个内存位置,则可以发出7个内存请求并等待它们。等待7个内存请求可能比等待单个内存请求要慢,但是可能要慢7倍?

苹果最新的笔记本电脑处理器M1显然具有很多内存级别的并行性。看起来单个内核具有大约28个级别的内存并行性,甚至可能更多。

如此高的内存级别并行度使我们朴素的随机内存模型无法应用。

为了进行测试,我设计了以下基准测试,比较了三个功能。第一个只是获取随机选择的字节对,并在将它们添加到计数器之前计算它们之间的按位XOR:

for(size_t i = 0; i< 2 * M; i + = 2){ 答案+ =数组[random [i]] ^数组[random [i + 1]]; }

for(size_t i = 0; i< 3 * M; i + = 3){ 答案+ =数组[random [i]] ^数组[random [i + 1]] ^ array [随机[i + 2]]; }

我们的朴素的内存访问成本模型预测第二个功能应该贵50%。但是,许多其他模型(例如简单的指令数)也会预测50%的开销。

为了让我们的天真内存访问模型能够物有所值,让我们抛出一个2维版本,该版本也可以访问附近的值(偏移一个字节):

for(size_t i = 0; i< 2 * M; i + = 2){ int idx1 =随机[i]; int idx2 =随机[i + 1]; 答案+ =数组[idx1] ^数组[idx1 + 1] ^ array [idx2] ^ array [idx2 + 1]; }

我们的幼稚内存访问成本模型会预测第一个功能和最后一个功能应具有大约相同的运行时间,而第二个功能应比后者贵50%。

让我们来衡量一下。我使用一个1GB的阵列,并报告每次迭代花费的平均时间(以纳秒为单位)。

乍一看,我们的幼稚内存访问模型得到了验证:3级功能比2级功能贵46%。但是我们不应该感到惊讶,因为大多数合理的模型都会做出这样的预测,因为该函数几乎在所有方面都完成了50%的工作。

比较两个2维函数更有趣……最后一个比第一个2维函数贵40%。它与我们的预测相矛盾。因此,至少在这种情况下,我们的简单内存访问成本模型使我们无法在Apple M1处理器上使用。

我的源代码可用。在这样的测试中,运行间差异较大,但是在我的Apple M1系统上结论很可靠。

重要的是,我并不预测其他系统会遵循相同的模式。请不要在非M1 PC上运行此基准测试,并期待可比的结果。

该基准测试旨在在具有M1处理器的Apple MacBook上运行,该处理器使用Apple的clang编译器进行编译。它不打算在其他系统上使用。