AVX
处理avx向量(数组)的时候,最好以bit的单位去看每个数。
指令集查询连接
https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html
SSE
SSE指令集是英特尔提供的基于SIMD(单指令多数据,也就是说同一时间内,对多个不同的数据执行同一条命令)的硬件加速指令,通过使用寄存器来进行并行加速。经过几代的迭代,最新的SSE4已经极大地扩展了指令集的功能,并且随后已经从128位寄存器继续扩展到256位的指令。
#include <mmintrin.h> //mmx, 4个64位寄存器
#include <xmmintrin.h> //sse, 8个128位寄存器
#include <emmintrin.h> //sse2, 8个128位寄存器
#include <pmmintrin.h> //sse3, 8个128位寄存器
#include <smmintrin.h> //sse4.1, 8个128位寄存器
#include <nmmintrin.h> //sse4.2, 8个128位寄存器
#include <immintrin.h> // avx, 16个256位寄存器
字节对齐
Advanced Vector Extensions。较新的Intel CPU都支持AVX指令集, 它可以一次操作256bit数据, 是SSE的2倍,可以使用一条AVX指令一次计算8个float数。AVX指令要求内存地址对齐于32字节边界。
_mm_prefetch
for(i=0;i a[i] = b[i]+1;
上面的循环中通过表达式a[j]访问数组,编译器会插入软件预取指令来把a[j+d]加载进缓存中,其中d由编译器分析后决定。
还可以通过SSE Intrinsics来支持软件预取,当然,这必须是在支持SSE扩展的处理器上才发挥作用;
void _mm_prefetch(char const *a,int sel);
它对应着PREFETCH指令,告诉处理器把地址a对应的缓存加载到更高速的缓存中,sel给出了预取操作的类型,如下所示:
PREFETCHINTA _MM_HINT_NTA 采用非临时预取,减少缓存行的污染
PREFETCH0 _MM_HINT_T0 预取数据到所有缓存
PREFETCH1 _MM_HINT_T1 预取到L2,L3缓存,但是不到L1缓存
PREFETCH2 _MM_HINT_T2 仅预取数据到L3缓存
AVX
数据类型 描述
__m128 包含4个float类型数字的向量
__m128d 包含2个double类型数字的向量
__m128i 包含若干个整型数字的向量
__m256 包含8个float类型数字的向量
__m256d 包含4个double类型数字的向量
__m256i 包含若干个整型数字的向量
_mm256_xor_si256 按位异或
#### Synopsis
__m256i _mm256_xor_si256 (__m256i a, __m256i b)
#include <immintrin.h>
Instruction: vpxor ymm, ymm, ymm
CPUID Flags: AVX2
#### Description
Compute the bitwise XOR of 256 bits (representing integer data) in a and b, and store the result in dst.
#### Operation
dst[255:0] := (a[255:0] XOR b[255:0])
dst[MAX:256] := 0
_m256_load_
用_m256_load_*把数据从内存加载到__mm256变量里,要求内存里的数据必须按32位对齐,否则会报段错误;用 m256_loadu*来加载不一定对齐的数据;
_mm256_storeu_si256
Moves values from a integer vector to an unaligned memory location.
Synopsis
void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
#include <immintrin.h>
Instruction: vmovdqu m256, ymm
CPUID Flags: AVX
Description
Store 256-bits of integer data from a into memory. mem_addr does not need to be aligned on any particular boundary.
Operation
MEM[mem_addr+255:mem_addr] := a[255:0]
#### Synopsis
void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
#include <immintrin.h>
Instruction: vmovdqu m256, ymm
CPUID Flags: AVX
#### Description
Store 256-bits of integer data from a into memory. mem_addr does not need to be aligned on any particular boundary.
#### Operation
MEM[mem_addr+255:mem_addr] := a[255:0]
__m256i _mm256_load_si256
https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html
__m256i _mm256_load_si256 (__m256i const * mem_addr)
Synopsis
__m256i _mm256_load_si256 (__m256i const * mem_addr)
#include <immintrin.h>
Instruction: vmovdqa ymm, m256
CPUID Flags: AVX
#### Description
Load 256-bits of integer data from memory into dst. mem_addr must be aligned on a 32-byte boundary or a general-protection exception may be generated.
#### Operation
dst[255:0] := MEM[mem_addr+255:mem_addr]
dst[MAX:256] := 0
__m256i _mm256_maskz_loadu_epi8
#### Synopsis
__m256i _mm256_maskz_loadu_epi8 (__mmask32 k, void const* mem_addr)
#include <immintrin.h>
Instruction: vmovdqu8 ymm {z}, m256
CPUID Flags: AVX512BW + AVX512VL
#### Description
Load packed 8-bit integers from memory into dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). mem_addr does not need to be aligned on any particular boundary.
#### Operation
FOR j := 0 to 31
i := j*8
IF k[j]
dst[i+7:i] := MEM[mem_addr+i+7:mem_addr+i]
ELSE
dst[i+7:i] := 0
FI
ENDFOR
dst[MAX:256] := 0
_mm256_mask_storeu_epi8
#### Synopsis
void _mm256_mask_storeu_epi8 (void* mem_addr, __mmask32 k, __m256i a)
#include <immintrin.h>
Instruction: vmovdqu8 m256 {k}, ymm
CPUID Flags: AVX512BW + AVX512VL
#### Description
Store packed 8-bit integers from a into memory using writemask k. mem_addr does not need to be aligned on any particular boundary.
#### Operation
FOR j := 0 to 31
i := j*8
IF k[j]
MEM[mem_addr+i+7:mem_addr+i] := a[i+7:i]
FI
ENDFOR
__m256i sum = ...;
int32_t* s=(int32_t*) ∑
for (int i=0;i<8;i++){
printf("s[%d]:%d \n",i,s[i]);
_mm256_alignr_epi32
__m256i _mm256_alignr_epi32 (__m256i a, __m256i b, const int imm8)
#### Synopsis
__m256i _mm256_alignr_epi32 (__m256i a, __m256i b, const int imm8)
#include <immintrin.h>
Instruction: valignd ymm, ymm, ymm, imm8
CPUID Flags: AVX512F + AVX512VL
#### Description
Concatenate a and b into a 64-byte immediate result, shift the result right by imm8 32-bit elements, and store the low 32 bytes (8 elements) in dst.
#### Operation
temp[511:256] := a[255:0]
temp[255:0] := b[255:0]
temp[511:0] := temp[511:0] >> (32*imm8[2:0])
dst[255:0] := temp[255:0]
dst[MAX:256] := 0
Description:
// imm8只取末尾三个字节计算长度,表示 imm8 个 32-bit, 以32个bit 为一个单元。相当于向右移动了imm8个 int32_t
Concatenate a and b into a 64-byte immediate result, shift the result right by imm8 32-bit elements, and store the low 32 bytes (8 elements) in dst.
Operation:
temp[511:256] := a[255:0]
temp[255:0] := b[255:0]
temp[511:0] := temp[511:0] >> (32*imm8[2:0])
dst[255:0] := temp[255:0]
dst[MAX:256] := 0
_mm256_sub_epi32
_mm256_sub_epi32 (__m256i a, __m256i b)
#### Synopsis
__m256i _mm256_sub_epi32 (__m256i a, __m256i b)
#include <immintrin.h>
Instruction: vpsubd ymm, ymm, ymm
CPUID Flags: AVX2
#### Description
Subtract packed 32-bit integers in b from packed 32-bit integers in a, and store the results in dst.
#### Operation
FOR j := 0 to 7
i := j*32
dst[i+31:i] := a[i+31:i] - b[i+31:i]
ENDFOR
dst[MAX:256] := 0
_mm256_and_si256
__m256i _mm256_and_si256 (__m256i a, __m256i b)
### Synopsis
__m256i _mm256_and_si256 (__m256i a, __m256i b)
#include <immintrin.h>
Instruction: vpand ymm, ymm, ymm
CPUID Flags: AVX2
#### Description
Compute the bitwise AND of 256 bits (representing integer data) in a and b, and store the result in dst.
#### Operation
dst[255:0] := (a[255:0] AND b[255:0])
dst[MAX:256] := 0
_mm256_loadu_si256
__m256i _mm256_loadu_si256 (__m256i const * mem_addr)
#### Synopsis
__m256i _mm256_loadu_si256 (__m256i const * mem_addr)
#include <immintrin.h>
Instruction: vmovdqu ymm, m256
CPUID Flags: AVX
#### Description
Load 256-bits of integer data from memory into dst. mem_addr does not need to be aligned on any particular boundary.
#### Operation
dst[255:0] := MEM[mem_addr+255:mem_addr]
dst[MAX:256] := 0
_mm256_add_epi32
#### Synopsis
__m256i _mm256_add_epi32 (__m256i a, __m256i b)
#include <immintrin.h>
Instruction: vpaddd ymm, ymm, ymm
CPUID Flags: AVX2
#### Description
Add packed 32-bit integers in a and b, and store the results in dst.
#### Operation
FOR j := 0 to 7
i := j*32
dst[i+31:i] := a[i+31:i] + b[i+31:i]
ENDFOR
dst[MAX:256] := 0
打印AVX相关寄存器的值
void Print(__m256i & value)
{
int32_t* s=(int32_t*) &value;
for (int i=0;i<8;i++){
printf("s[%d]:%d, ",i,s[i]);
}
printf("\n");
}
#include<stdio.h>
#include"immintrin.h"
void sum(){
__m256i a=_mm256_set_epi32(20,30,40,60,342,34523,474,123);
__m256i b=_mm256_set_epi32(234,234,456,78,2345,213,76,88);
__m256i sum=_mm256_add_epi32(a,b);
int32_t* s=(int32_t*) ∑
for (int i=0;i<8;i++){
printf("s[%d]:%d \n",i,s[i]);
}
}
s[0]:211
s[1]:550
s[2]:34736
s[3]:2687
s[4]:138
s[5]:496
s[6]:264
s[7]:254
GCC march
# gcc -march=skylake-avx512 -dM -E - < /dev/null | egrep "SSE|AVX" | sort
#define __AVX__ 1
#define __AVX2__ 1
#define __AVX512BW__ 1
#define __AVX512CD__ 1
#define __AVX512DQ__ 1
#define __AVX512F__ 1
#define __AVX512VL__ 1
#define __SSE__ 1
#define __SSE2__ 1
#define __SSE2_MATH__ 1
#define __SSE3__ 1
#define __SSE4_1__ 1
#define __SSE4_2__ 1
#define __SSE_MATH__ 1
#define __SSSE3__ 1
-march
-march 是 GCC 编译器的一个参数,用于指定生成代码的目标 CPU 架构。使用 -march=native 时,GCC 会使用当前运行编译器的 CPU 的指令集。
如果你想知道在没有明确指定 -march 参数时,GCC 默认会使用哪个 CPU 架构,这依赖于 GCC 的版本以及它是在什么操作系统上运行的。通常,如果不指定 -march 参数,GCC 会将目标设置为它在编译时可用的最通用的指令集。
例如,在一个使用 x86_64 架构的 Linux 系统上,如果你没有指定 -march 参数,GCC 可能会默认使用的是支持 SSSE3 指令集的 CPU。
如果你想要查看 GCC 编译器在你的系统上没有明确 -march 参数时默认使用的是哪个 CPU 架构,你可以编译一个小程序并使用 gcc -v 来查看编译器的输出信息,其中会包含它选择的默认架构。
// test.cpp
int main() {
return 0;
}
$ g++ -v -o test test.cpp
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
COLLECT_GCC_OPTIONS='-v' '-o' 'test' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1plus -quiet -v -D_GNU_SOURCE test.cpp -quiet -dumpbase test.cpp -mtune=generic -march=x86-64 -auxbase test -version -o /tmp/ccvCGz7X.s
GNU C++ (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-44) (x86_64-redhat-linux)
compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-44), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/x86_64-redhat-linux
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/backward
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
/usr/local/include
/usr/include
End of search list.
GNU C++ (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-44) (x86_64-redhat-linux)
compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-44), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 60e1dcb67eec7f22a25d4f77ffd24e56
COLLECT_GCC_OPTIONS='-v' '-o' 'test' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
as -v --64 -o /tmp/ccdJOIJG.o /tmp/ccvCGz7X.s
GNU assembler version 2.27 (x86_64-redhat-linux) using BFD version version 2.27-44.base.tl2.1.1
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'test' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o test /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccdJOIJG.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
- -march=x86-64和-march=native选项都可以针对特定的处理器架构进行优化,但它们之间有一些区别:
-march=x86-64: 这个选项告诉编译器生成适用于x86-64架构的代码。这意味着编译器会生成能在几乎所有支持x86-64的处理器上运行的代码,但不会针对任何特定的处理器进行优化。因此,它是一种比较通用的选项,适用于在多个不同类型的处理器上运行的程序。
-march=native: 这个选项告诉编译器使用本地系统的处理器类型来生成代码。编译器会检测当前系统的处理器类型,并尽可能地优化生成的代码以利用该处理器的特性。这样做的好处是,生成的代码可以充分利用当前系统的硬件特性,从而可能实现更好的性能。然而,由于代码是针对特定的处理器类型生成的,因此生成的可执行文件可能无法在其他类型的处理器上运行。
所以,性能高低的比较要取决于具体的情况。如果你要在多台不同类型的处理器上运行程序,并且希望保持较好的兼容性,则使用-march=x86-64可能更合适。如果你只关心在当前系统上获得最佳性能,并且不需要考虑可移植性,则可以尝试使用-march=native选项。