我正在尝试使用 perf 工具分析我的 C++ 代码。实现包含带有 SSE/AVX/AVX2 指令的代码。除此之外,代码还使用 -O3 -mavx2 -march=native
标志进行编译。我相信 __memset_avx2_unaligned_erms
函数是 memset
的 libc 实现。 perf 表明这个函数有相当大的开销。函数名称表示内存未对齐,但是在代码中我使用 GCC 内置宏 __attribute__((aligned (x)))
显式对齐内存显着的开销以及为什么调用未对齐的版本,尽管内存是显式对齐的?
最佳答案
不,它没有。 这意味着 glibc 在该硬件上选择的 memset 策略不会尝试在小规模情况下完全避免对齐访问。 (glibc 在动态链接器符号解析时选择一个 memset 实现,因此它在第一次调用后得到运行时调度而没有额外的开销。)
如果您的缓冲区实际上是对齐的并且大小是 vector 宽度的倍数,那么所有访问都将对齐并且基本上没有开销。 (将 vmovdqu
与恰好在运行时对齐的指针一起使用完全等同于所有支持 AVX 的 CPU 上的 vmovdqa
。)
对于大型缓冲区,它仍会在主循环之前对齐指针,以防它未对齐,与仅适用于 32 字节的实现相比,需要一些额外的指令对齐的指针。 (但看起来它使用了 rep stosb
而没有对齐指针,如果它根本就不会指向 rep stosb
。)
gcc+glibc 没有特殊版本的 memset,只能用对齐的指针调用。 (或用于不同对齐保证的多个特殊版本)。 GLIBC 的 AVX2 未对齐实现适用于对齐和未对齐的输入。
它在 glibc/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S
中定义,它定义了几个宏(比如将 vector 大小定义为 32)然后 #includes "memset-vec-unaligned-erms.S"
.
源码中的注释是这样说的:
/* memset is implemented as:
1. Use overlapping store to avoid branch.
2. If size is less than VEC, use integer register stores.
3. If size is from VEC_SIZE to 2 * VEC_SIZE, use 2 VEC stores.
4. If size is from 2 * VEC_SIZE to 4 * VEC_SIZE, use 4 VEC stores.
5. If size is more to 4 * VEC_SIZE, align to 4 * VEC_SIZE with
4 VEC stores and store 4 * VEC at a time until done. */
主循环之前的实际对齐是在一些 vmovdqu
vector 存储之后完成的(如果用于实际对齐的数据则没有惩罚:https://agner.org/optimize/):
L(loop_start):
leaq (VEC_SIZE * 4)(%rdi), %rcx # rcx = input pointer + 4*VEC_SIZE
VMOVU %VEC(0), (%rdi) # store the first vector
andq $-(VEC_SIZE * 4), %rcx # align the pointer
... some more vector stores
... and stuff, including storing the last few vectors I think
addq %rdi, %rdx # size += start, giving an end-pointer
andq $-(VEC_SIZE * 4), %rdx # align the end-pointer
L(loop): # THE MAIN LOOP
VMOVA %VEC(0), (%rcx) # vmovdqa = alignment required
VMOVA %VEC(0), VEC_SIZE(%rcx)
VMOVA %VEC(0), (VEC_SIZE * 2)(%rcx)
VMOVA %VEC(0), (VEC_SIZE * 3)(%rcx)
addq $(VEC_SIZE * 4), %rcx
cmpq %rcx, %rdx
jne L(loop)
所以当 VEC_SIZE = 32 时,它会将指针对齐 128。这太过分了;缓存行是 64 字节,实际上只要与 vector 宽度对齐就可以了。
它还有一个使用 rep stos
的阈值,如果启用并且缓冲区大小 > 2kiB,在具有 ERMSB 的 CPU 上。 (Enhanced REP MOVSB for memcpy)。
关于c++ - 性能报告显示此函数 "__memset_avx2_unaligned_erms"有开销。这是否意味着内存未对齐?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51614543/