c-为什么glibc memcpy不选择avx512版本



我编译了以下示例代码:

#cat array_addition.c 
#define MAX 1000000
#define S 1024
#include <string.h>
int a[S], b[S], c[S];
__attribute__((target_clones("avx512f", "avx2","arch=atom","default")))
void foo(int argc){
int i,x;
for (x=0; x<1024; x++){
for (i=0; i<S; i++){
a[i] = b[i] + c[i];
}
}
b[0] = argc;
memcpy(&a[0], &b[0], argc *sizeof(int));
}
int main(int argc, char** argv) {
foo(argc);
return 0;
}

它们调用memcpy;

从objdump中,我们可以发现它将调用GLIBCmemcpy:

#readelf -r a.out 
Relocation section '.rela.dyn' at offset 0x418 contains 1 entry:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000403ff8  000200000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x430 contains 4 entries:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000404018  000100000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000404020  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000404028  000300000007 R_X86_64_JUMP_SLO 0000000000000000 memcpy@GLIBC_2.14 + 0
000000404030  000000000025 R_X86_64_IRELATIV                    4018f0

然后,我使用gdb来跟踪它使用了哪个glibc实现;

(gdb) b memcpy@plt    
Breakpoint 1 at 0x401050
(gdb) s
The program is not being run.
(gdb) r
Starting program: /root/a.out 
Breakpoint 1, 0x0000000000401050 in memcpy@plt ()
(gdb) s
Single stepping until exit from function memcpy@plt,
which has no line number information.
0x00007ffff7b623a0 in __memcpy_ssse3_back () from /lib64/libc.so.6
(gdb) info function __memcpy_*
All functions matching regular expression "__memcpy_*":
Non-debugging symbols:
0x00007ffff7aa2840  __memcpy_chk_sse2
0x00007ffff7aa2850  __memcpy_sse2
0x00007ffff7ab1b40  __memcpy_chk_avx512_no_vzeroupper
0x00007ffff7ab1b50  __memcpy_avx512_no_vzeroupper
0x00007ffff7b23360  __memcpy_chk
0x00007ffff7b5a470  __memcpy_chk_ssse3
0x00007ffff7b5a480  __memcpy_ssse3
0x00007ffff7b62390  __memcpy_chk_ssse3_back
0x00007ffff7b623a0  __memcpy_ssse3_back
(gdb) 

存在__memcpy_avx512no-zeroupper,但未被选择;

我的cpu支持它的功能:

标志:fpu vme de pse tsc msr pae mce cx8 apic sepmtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss httm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebsbts rep_good nopl xtopology nontop_tsc cpuid aperfperf pni pclmulqdqdtes64监视器ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcmpcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aesxsave avx f16c rdrand lahf_lm abm 3dnow预取cpuid_fault epb cat_l3cdp_l3 invpcid_单个pti intel_ppin ssbd mba ibrs ibpb stibptpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1hle avx2 smep bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dqrdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vlxsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occupt_llc cqm_mbm_totalcqm_mbm_local dterm ida arat pln pts pku ospke flush_l1d

gcc版本:

使用内置规格。COLLECT_GCC=GCCCOLLECT_LTO_WRAPPER=/root/china-gcc-10.2.0/libexec/gcc/x86_64-pc-linux-gnu1.2.0/LTO-WRAPPER目标:x86_64-pc-linux-gnu配置为:/配置--prefix=/root/china-gcc-10.2.0--禁用multilib线程模型:posix支持的LTO压缩算法:zlib gcc 10.2.0版本(gcc(

打开"主流;像Skylake-X和IceLake这样的CPU,如果你在程序的大部分运行时间里都一致使用512位矢量,而不仅仅是偶尔的内存,那么就完全值得使用512位向量。(此外,如果你的程序将运行很长时间,否则你会通过上下文切换和/或超线程来减慢共享同一物理核心的其他进程的速度。(有关详细信息,请参阅降低CPU频率的SIMD指令:你不希望偶尔调用memcpy来将CPU频率保持在较低的最大turbo。

使用具有256位矢量的AVX-512功能(AVX-512VL(在某些方面是值得的,例如,如果掩蔽很好,或者如果您使用YMM16..31来避免VZEROUPPER。

我猜glibc只会在像Knight’s Landing(KNL(Xeon Phi这样的系统上将memcpy解析为__memcpy_avx512_no_vzeroupper,其中CPU是围绕AVX-512设计的,使用512位ZMM矢量没有缺点。即使在KNL上使用ymm0..15之后,也不需要vzerooper。事实上,vzerooper在KNL上的速度非常慢,这肯定是需要避免的,因此将no_vzeroupper放在函数名称中。

https://code.woboq.org/userspace/glibc/sysdeps/x86_64/multiarch/memmove-avx512-no-vzeroupper.S.html是该版本的源。它使用ZMM矢量,包括ZMM0..15,所以如果在Skylake/IceLake CPU上使用,应该使用vzerooper这个版本看起来是为KNL设计的


如果AVX-512VL版本使用ymm16..31来避免vzerooper(以加速32..64字节的拷贝(,而不使用ZMM寄存器,则会有一些微小的好处。

__memcpy_avx512_no_vzeroupper只使用ZM16..31是有意义的,因此在主流CPU上避免vzerooper不是问题;那么在已经大量使用AVX-512(因此已经支付了CPU频率成本(的代码中,它将是一个可用的选项

最新更新