Chi*_*ing 2 c glibc compiler-optimization 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;
}
Run Code Online (Sandbox Code Playgroud)
其中调用 memcpy;
从 objdump 中,我们可以发现它会调用 GLIBC memcpy:
#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
Run Code Online (Sandbox Code Playgroud)
然后,我使用 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)
Run Code Online (Sandbox Code Playgroud)
有__memcpy_avx512_no_zeroupper,但没有被选中;
我的 CPU 支持它的功能:
标志:FPU VME德PSE TSC MSR PAE MCE CX8 APIC月MTRR PGE MCA CMOV轻拍PSE36 CLFLUSH DTS ACPI MMX FXSR SSE SSE2 SS HT TM PBE系统调用的nx pdpe1gb rdtscp流明constant_tsc技术arch_perfmon PEBS BTS rep_good nopl xtopology nonstop_tsc CPUID aperfmperf PNI pclmulqdq dtes64监视ds_cpl VMX SMX EST TM2 SSSE3 SDBG FMA CX16 xtpr PDCM PCID DCA sse4_1 sse4_2 x2apic movbe POPCNT tsc_deadline_timer AES XSAVE AVX F16C rdrand lahf_lm ABM 3dnowprefetch cpuid_fault EPB cat_l3 cdp_l3 invpcid_single PTI intel_ppin SSBD MBA IBRS ibpb stibp tpr_shadow vnmi FlexPriority可EPT VPID ept_ad fsgsbase tsc_adjust BMI1 HLE AVX2 SMEP bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xsavec xgetbqm_pmc_mbtm_pmc_m_p_m_p_m_p_m_p_m_p_m_p_mc_m_p_m_p_m_qlcuptc_mbqm_pllcupdc_mc_pllcupdc_mbqm_pllcpus冲洗_l1d
gcc 版本:
使用内置规范。COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/root/china-gcc-10.2.0/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper 目标:x86_64-pc-linux-gnu 配置:./configure --prefix=/root/china-gcc-10.2.0 --disable-multilib 线程模型:posix 支持的 LTO 压缩算法:zlib gcc version 10.2.0 (GCC)
在像 Skylake-X 和 IceLake 这样的“主流”CPU 上,如果您在程序的大部分运行时间中始终如一地使用 512 位向量,而不仅仅是偶尔的 memcpy,那么只值得使用 512 位向量。(而且如果您的程序将运行很长时间,否则您会通过上下文切换和/或超线程减慢共享相同物理核心的其他进程。)有关详细信息,请参阅降低 CPU 频率的 SIMD 指令:您没有希望偶尔调用 memcpy 以将您的 CPU 频率降低到较低的最大涡轮增压。
使用带有 256 位向量 (AVX-512VL) 的 AVX-512 功能在某些方面是值得的,例如,如果屏蔽很好,或者如果您使用 YMM16..31 来避免 VZEROUPPER。
我猜想 glibc 只会__memcpy_avx512_no_vzeroupper在像 Knight's Landing (KNL) Xeon Phi 这样的系统上解析 memcpy ,其中 CPU 是围绕 AVX-512 设计的,并且使用 512 位 ZMM 向量没有缺点。即使在 KNL 上使用 ymm0..15 之后,也不需要 vzeroupper。事实上,vzeroupper 在 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 上使用它应该使用 vzeroupper。 这个版本看起来是为 KNL 设计的。
使用 ymm16..31 避免 vzeroupper(加速 32 .. 64 字节副本)的 AVX-512VL 版本会有一些微小的好处,而无需使用 ZMM 寄存器。
并且__memcpy_avx512_no_vzeroupper只使用 ZMM16..31 是有意义的,因此在主流 CPU 上避免 vzeroupper 不是问题;那么这将是已经大量使用 AVX-512 的代码中的一个可用选项(因此已经支付了 CPU 频率成本。)