AVX and SIMD

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*) &sum;
    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*) &sum;
    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选项。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值