【发布时间】:2018-05-02 14:07:03
【问题描述】:
所以我使用 c2go 将 C 代码与 Go 链接。 C 代码要求从 Go 调用的函数的某些参数是 256 位对齐的(函数参数都是指向 Go 变量的指针)。 Go有没有办法实现这一点(即为Go中的变量指定256对齐)?
在 Go 中,“unsafe.Alignof(f)”显示为 8 个字节对齐(对于“var f [8]float32”),即 Go 保证 f 仅对齐 8 个字节。我需要它以某种方式对齐 32 个字节。
对于好奇的人: C 代码使用 SIMD 指令(具体为 AVX)。我使用的是“vmovaps”指令,它需要操作数 256 位对齐。我可以使用不需要对齐的“vmovups”,但我怀疑这会降低性能。
【问题讨论】:
-
AVX 中未对齐负载的性能损失通常可以忽略不计(与 10 多年前在旧 CPU 上使用 SSE 的未对齐负载不同)。
-
Go 保证
var f [8]float32的最小对齐属性是4而不是8: Size and alignment guarantees。此外,var f [8]float32; fmt.Println(unsafe.Alignof(f))的输出是4而不是8:play.golang.org/p/ok2IvK0214 -
仅当您跨越缓存行或页面边界时,未对齐的加载才会产生额外成本。这对于页面拆分很重要(尤其是在 Skylake 以外的 CPU 上),而对于 Haswell 及更高版本的缓存行拆分则相当小。 (@PaulR:splitting unaligned 256b loads into vmovups / vinsertf128 is actually a win on Sandybridge/Ivybridge, if the data really is misaligned at runtime,而不是在构建时不知道对齐。
-
Gcc 启用
-mavx256-split-unaligned-load和-mtune=generic,即使使用-mavx2,因为 there's no option to "tune for all CPUs that could run this code"。 :( 这就是为什么如果你正在为 Haswell 构建,使用gcc -march=haswell而不是gcc -mavx2是一个非常好的主意。(clang 从不做这种分裂的东西。)另外,gcc 现在可能应该放弃它,因为它不是在 SnB 上大获全胜,但在 HSW 上损失惨重,数据确实是一致的。 -
TL:DR:如果您可以对所有数据使用对齐分配,请执行此操作。只要数据在运行时对齐,您在其上运行的代码是否使用未对齐错误的指令或在硬件中处理它都没有关系。这对于 AVX512(其中向量大小 = 缓存线大小,因此未对齐意味着每个负载都跨越缓存线边界)比 AVX2 重要得多。对于在内存或 L3 高速缓存中循环阵列,仅 16B 对齐或更少与 AVX2 对齐通常不是可测量的减速。对于 AVX2,与 AVX512 不同,未对齐可能只是 L1D 缓存中数据热的问题。