performance - x86_64 和 ARM64 上对齐与非对齐内存访问的不同运行时
问题描述
我创建了一个简单的演示来展示未对齐的内存存储/加载在 x86_64 和 ARM64 架构上通常不是原子的。该演示包含一个创建两个线程的 C++ 程序——第一个十亿次调用一个名为 的函数store
,第二个对一个名为 的函数执行相同的操作load
。该程序的源代码在这里:
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <thread>
extern "C" void store(void*);
extern "C" uint16_t load(void*);
alignas(64) char buf[65];
char* ptr;
static long n = 1'000'000'000L;
void f1()
{
for (long i = 0; i < n; i++)
store(ptr);
}
void f2()
{
long v0x0000 = 0;
long v0x0101 = 0;
long v0x0100 = 0;
long v0x0001 = 0;
long other = 0;
for (long i = 0; i < n; i++)
{
uint16_t a = load(ptr);
if (a == 0x0000) v0x0000++;
else if (a == 0x0101) v0x0101++;
else if (a == 0x0100) v0x0100++;
else if (a == 0x0001) v0x0001++;
else other++;
}
std::cout << "0x0000: " << v0x0000 << std::endl;
std::cout << "0x0101: " << v0x0101 << std::endl;
std::cout << "0x0100: " << v0x0100 << std::endl;
std::cout << "0x0001: " << v0x0001 << std::endl;
std::cout << "other: " << other << std::endl;
}
int main(int arc, char* argv[])
{
int offset = std::atoi(argv[1]);
ptr = buf + offset;
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
}
和函数在汇编源文件中单独定义store
。load
对于 x86_64 如下:
.intel_syntax noprefix
.global store
.global load
.text
store:
mov eax, 0
mov WORD PTR [rdi], ax
mov eax, 0x0101
mov WORD PTR [rdi], ax
ret
load:
movzx eax, WORD PTR [rdi]
ret
并且,对于 ARM64 如下:
.global store
.global load
.text
store:
mov w1, 0x0000
strh w1, [x0]
mov w1, 0x0101
strh w1, [x0]
ret
load:
ldrh w0, [x0]
ret
当我运行程序时,一切都按预期工作。当我通过偏移量 0 时,存储/加载是对齐的,并且只是值0x0000
,并且0x0101
在读取线程中被观察到。当我通过偏移量 63 时,存储/加载未对齐并跨越缓存行边界,并且值0x0100
和0x0001
也被观察到。这适用于两种架构。
但是,我注意到这些测试运行的执行时间存在很大差异。我观察到的一些典型时间:
- x86_64 + 偏移量 0(对齐):6.9 [s]
- x86_64 + 偏移量 63(未对齐):28.3 [s]
- ARM64 + 偏移量 0(对齐):6.8 [s]
- ARM64 + 偏移量 63(未对齐):9.2 [s]
在x86_64上,当两个缓存行涉及未对齐的情况时,运行时间会慢几倍。但在ARM64上,运行时间稍慢一些。我想知道这两种架构之间的这种行为有何不同。(我对缓存一致性机制不太熟悉。)
用于实验的特定处理器是Intel Xeon E5-2680 v3和Cortex-A72。前者在双插槽服务器中,但我将两个线程都限制在单个插槽中(通过taskset
or numactl
)。后者在 Raspberry Pi 4 设备中。两个系统都运行 Linux,而且我使用 GCC 进行构建。
解决方案
推荐阅读
- apache-spark - Spark saveAsTable 抛出 NoSuchTableException
- c++ - VS 2019 中输入文件的路径
- python - 包含消息的未标记文本数据
- git - 执行 git commit --amend 后将更改拉入新分支
- django - Django对象相关和manyTomany,prefetch_related?
- java - Autowired 抛出空指针
- java - 使用 while 循环引导到一个数字
- version-control - Update the working directory with part of a changeset
- xslt-1.0 - xsl:sort 对缩进的副作用
- c - 可能是“malloc()”问题:Server to Visitor 消息是缺陷