gcc 在不支持avx512的情况下进行编译时,防止immintrin.h包含avx512头文件

7uzetpgm  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(537)

我正在编译没有AVX512支持,但我注意到immintrin. h为AVX512拖了大量的LOC,例如。

/usr/lib/gcc/x86_64-linux-gnu/11/include/avx512fintrin.h

我试过检查指定行军选项是否有帮助,但似乎没有帮助。

cat avx512_include.cpp 
#include <immintrin.h>
int main() {}

g++ -march=core2  -E -P avx512_include.cpp | wc -l
34365

g++ -march=skylake-avx512  -E -P avx512_include.cpp | wc -l
34257

我知道,我可以在理论上破解我的gcc安装,并祈祷它会工作时,我删除所有的内容从avx512头😈,但我正在寻找一个支持的方式,如果有一个。
我试着查看gcc头文件的内部,看看是否有一些宏检查来包含avx512头文件,但它们似乎是无条件包含的。
我试图找到一个gcc三月标签,找不到任何,如果有人知道更合适的标签旁边的电流请评论

6jjcrrmo

6jjcrrmo1#

愚者/clang支持__attribute__((target("avx512f"))),以便在编译文件时使用AVX-512内容,而不使用-march=,这意味着-mavx512f。因此immintrin.h仍必须拉入AVX 512定义。
但是,如果您尝试在不启用__m512i v = _mm512_set1_ps(1.0);的情况下使用它,仍然会收到来自愚者的编译错误和clang。

为了缩短编译时间,也许预编译头文件是一个选项。大多数Linux发行版默认情况下不这样做。
或者在根本不使用AVX-512的项目中,是的,你可以把东西砍下来,这样标头就不会被包括在内,或者至少不会被编译。
最不具侵入性的方法可能是在第一次包含文件之前定义愚者的include-guard宏。愚者的预处理器仍然会读取这些文件,但编译器本身不会在它们上面花费任何时间。从大小来看,avx 512 fintrin. h和avx 512 vlintrin. h是最大的宏; AMX和VNNI等都是小得多的头文件,所以可能只有avx512*.h才真正值得费心。

$ grep --no-filename '#define.*_INCLUDED$' /usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/include/avx512*
#define _AVX5124FMAPSINTRIN_H_INCLUDED
#define _AVX5124VNNIWINTRIN_H_INCLUDED
#define _AVX512BF16INTRIN_H_INCLUDED
...

也许把这个重定向到一个skip-avx512.h
但是,一些较新的文件定义了AVX-512版本的内部函数,因此,如果缺少__mmask8等的定义,则会破坏定义AVX-512内部函数 * 和 * 非AVX-512 VEX的gfniintrin.hvaesintrin.h等标头您可以通过添加所涉及的键类型的定义来解决这个问题,或者也完全跳过那些报头。

// Some headers aren't exclusively AVX-512 but have some AVX-512 intrinsics
#if 0
 #define _GFNIINTRIN_H_INCLUDED         // SSE / VEX / EVEX intrinsics
 #define __VAESINTRIN_H_INCLUDED        // wider VEX / EVEX versions of AES-NI
 #define _VPCLMULQDQINTRIN_H_INCLUDED   // wider VEX / EVEX versions of pclmul
#else  // keep those headers happy
 // These are the correct definitions for GCC as of 12.2, and highly unlikely to ever change.
 // Not that it should matter except for matching builtins
 typedef long long __m512i __attribute__((vector_size(64),may_alias));  // used in gfniintrin and vaesintrin.h
 typedef char __v64qi __attribute__ ((__vector_size__ (64)));  // used in gfniintrin.h
 typedef long long __v8di __attribute__ ((__vector_size__ (64)));  // used in vpclmulqdqintrin.h with optimization enabled
 __attribute__((target("avx512f"))) static inline __m512i
   _mm512_setzero_si512(void) { return (__m512i){0}; }  // the real definition is more verbose
 typedef unsigned char __mmask8;      // used in gfniintrin.h
 typedef unsigned short __mmask16;
 typedef unsigned __mmask32;
 typedef unsigned long long __mmask64;
#endif

这在我的Arch Linux系统上的GCC12.2上可以使用。编译你的空测试用例而不进行优化:

$ uname -a  # x86-64 Arch GNU/Linux, using their binary packages
Linux volta 6.0.10-arch2-1 #1 SMP PREEMPT_DYNAMIC Sat, 26 Nov 2022 16:51:18 +0000 x86_64 GNU/Linux
$ gcc -v
...
gcc version 12.2.0 (GCC) 

$ perf stat -r 20 gcc -c avx512-included.c   # vanilla, no extra #define stuff
   (average 265 ms on an i7-6700k at 3.9GHz)
$ perf stat -r 20 gcc -c avx512-excluded.c   # with _AVX512 #defines and the typedefs
   (average  98 ms on an i7-6700k at 3.9GHz)

启用优化后(Skylake上的-O2 -march=native),时间分别为295和79毫秒。
如果-O2没有任何arch=,则为322 ms vs. 105 ms。也许不必运行#pragma target()行是-march=更快的原因。定义_GFNIINTRIN_H_INCLUDED和其他两个将更快的时间降低到95 ms,因此显然这些相对较小的头文件产生了很大的差异。
-O2编译更多行可能会更慢,因为接受立即数的内部函数只是没有#ifdef __OPTIMIZE__的宏。(即使函数在任何地方都没有内联,也可能会花费一些时间进行优化。)
一个更激进的方法是创建一个/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/include/immintrin.h的定制版本,并简单地删除avx512*头文件的所有#include行,以及其他一些太新而不想使用的行,如avxvnniintrin.hamx*shaintrin.h
如果您不想完全跳过VAES、GFNI和VPCLMUL,则必须采取相同的预防措施。
或者把它命名为"custom_gcc_intrin.h"并包含它而不是immintrin.h?或者把它命名为immintrin.h,但把它放在特定项目中的某个地方,在那里它首先作为一个包含路径被查找?
不要修改immintrin.h的系统副本,或者设置默认使用修改后的版本;这可能会在将来的某个时候咬你,当你试图编译的东西,确实有AVX-512代码路径与运行时检测。你会超级困惑为什么你的愚者是坏的,如果你忘记了这个修改多年前。

相关问题