older answer表示aarch64支持未对齐的读/写,并提及性能成本,但不清楚答案是否仅涵盖ALU或SIMD(128位寄存器)操作。
相对于对齐的128位NEON加载和存储,在aarch64上未对齐的128位NEON加载和存储的速度有多慢(如果有的话)?
对于未对齐的SIMD加载和存储(如SSE2的情况)是否有单独的指令,或者已知对齐的加载/存储与可能未对齐的加载/存储相同的指令?
答案 0 :(得分:3)
根据 4.6加载/存储对齐部分中的Cortex-A57 Software Optimization Guide,它说:
ARMv8-A架构允许任意对齐多种类型的加载和存储访问。 Cortex-A57处理器可处理大多数未对齐的访问,而不会造成性能损失。但是,有些情况 减少带宽或产生额外的延迟,如下所述:
- 加载跨越缓存行(64字节)边界的操作
- 存储跨越16字节边界的操作
因此,它可能取决于您使用的处理器,故障(A57,A72,A-72,A-75)或按顺序(A-35,A-53,A-55)。我没有为有序处理器找到任何优化指南,但是他们确实有一个硬件性能计数器,您可以使用它来检查未对齐指令的数量是否会影响性能:
0xOF_UNALIGNED_LDST_RETIRED Unaligned load-store
这可以与perf
工具一起使用。
AArch64中没有针对未对齐访问的特殊说明。
答案 1 :(得分:2)
如果必须拆分加载/存储或使其超过缓存行,则至少需要一个额外的周期。
有一些详尽的表,它们指定Cortex-A8(有序)和Cortex-A9(部分为OoO)的各种对齐方式所需的周期数和寄存器数。例如,具有一个reg的vld1
对未对齐访问和64位对齐访问的处罚为1个周期。
Cortex-A55(按顺序)最多可以执行64位加载和128位存储,因此,its optimization manual的3.3节指出,以下情况会产生1个周期的罚款:
•跨越64位边界的加载操作
•跨越128位边界的128位存储操作
根据its optimization guide的第5.4节,Cortex-A75(OoO)会受到以下处罚:
•加载跨越64位边界的操作。
•在AArch64中,所有跨越128位边界的存储。
•在AArch32中,所有跨越64位边界的存储。
正如吉列尔莫(Guillermo)的回答,A57(OoO)会受到以下处罚:
•加载跨越缓存行(64字节)边界的操作
•存储跨越[128位]边界的操作
我有点怀疑A57不会因为跨越A55和A75而跨越64位边界而受到惩罚。所有这些都有64字节的高速缓存行。他们也应该对跨越缓存行有处罚。最后,请注意,这里有unpredictable behavior for split access crossing pages。
从使用Cavium ThunderX进行的一些粗略测试(无性能计数器)来看,罚金似乎接近2个周期,但这可能是背靠背的未对齐负载和循环存储的累加效果
AArch64 NEON指令不能区分对齐和未对齐(例如,请参见LD1)。对于AArch32 NEON,在寻址(VLDn)中静态指定对齐方式:
vld1.32 {d16-d17}, [r0] ; no alignment
vld1.32 {d16-d17}, [r0@64] ; 64-bit aligned
vld1.32 {d16-d17}, [r0:64] ; 64 bit-aligned, used by GAS to avoid comment ambiguity
我不知道没有对齐限定符的对齐访问是否比在以AArch32模式运行的最新芯片上使用对齐限定符的访问慢。 ARM的一些旧文档鼓励尽可能使用限定符。 (通过比较,英特尔改进了它们的芯片,使未对齐和对齐的移动在对齐地址时的性能相同。)
如果您使用内在函数,则MSVC具有后缀_ex
的变体,可以接受对齐方式。使__builtin_assume_aligned
成为GCC发出对齐限定符的可靠方法。
// MSVC
vld1q_u16_ex(addr, 64);
// GCC:
addr = (uint16_t*)__builtin_assume_aligned(addr, 8);
vld1q_u16(addr);
答案 2 :(得分:0)
在aarch64上不使用对齐提示。它们是透明的。如果指针与数据类型的大小对齐,则性能优势是自动的。
如有疑问,对于GCC / Clang,请在变量声明上使用__attribute__((__aligned__(16)))
。