查看CPU相关信息



执行结果举例:

查看电脑CPU支持的指令集:

cat /proc/cpuinfo | grep "processor" | wc -l

支持的指令集:

向量指令集

Flynn分类法根据指令数据进入CPU的方式,将计算机 架构分为四种不同的类型。

  • 1.单指令流单数据流(SISD,Single Instruction stream Single Data stream)
  • 2.单指令流多数据流(SIMD,Single Instruction stream Multiple Data stream)
  • 3.多指令流单数据流(MISD,Multiple Instruction stream Single Data stream)
  • 4.多指令流多数据流(MIMD,Multiple Instruction stream Multiple Data stream)

举例:

SISD指令:需要多条指令完成相关操作

SIMD指令:只需要单条指令就可完成相关操作

API 介绍

官方参考文档见:Intel Intrinsics Guide

需要什么功能,可对应该文档查看对应的API

因API太多,整个左侧区域是用来筛选,左侧上方的选择区域使用来按指令集来筛选,左测下方的选择区域是用来按功能筛选。

数据类型

可简单理解为,MMX对应64位,SSE对应128位,AVX对应256位

intel 指令集的变量命名规则位__mm<bit_width><data_type>,其中bit_width即数据长度(64|128|256),data_type即存储的数据是什么类型(i|d),i对应int,d对应double,要是空,则对应float

数据类型 描述 大小
__m64 包含2个单精度(float)浮点数的64位向量 2 x 32 bit
__m128 包含4个单精度浮点数的128位向量 4 x 32 bit
__m128i 包含数个整型数值的128位向量 128 bit
__m128d 包含2个双精度浮点数的128位向量 2 x 64 bit
__m256 包含8个单精度浮点数的256位向量 8 x 32 bit
__m256i 包含数个整型数值的256位向量 256 bit
__m256d 包含4个双精度浮点数的256位向量 4 x 256 bit

SSE Data Type(16 XMM Registers)

AVX Data Types(16 YMM Registers)

注:

AVX的256位寄存器和SSE的128位寄存器存在着相互重叠的关系(XMM寄存器位YMM寄存器的低位),所以最好不要混用AVX与SSE指令集,否则会导致transition penalty(过度处罚),慢50~80时钟周期

函数类型:

SSE/AVX intrinsic functions的命名格式位_mm<bit_width>_<name>_<data_type>

  • <bit_width>:表明向量的位长度,对于128位的向量,这个参数为空;256位的向量,这个参数位256
  • <name>:描述内联函数的算术操作
  • <data_type>:标识函数主参数的数据类型。
  • ps 包含float 类型的向量,把32 bits当作一个数
  • pd包含double类型的向量,把64bits当作一个数
  • epi8/epi16/epi32/epi64 :向量中每个数都是整型,包含8位/16位/32位/64位的有符号整数
  • epu8/epu16/epu32/epu64: 向量中每个数都是无符号整型(unsigned),包含8位/16位/32位/64位的无符号整数
  • m128/m128i/m128d/m256/m256i/m256d:输入值与返回类型不同时,标识输入向量类型

    例如:__m256i _mm256_setr_m128i(__m128i lo,__m128i hi) 输入两个__m128i向量,把他们拼在一起,变成一个__m256i返回,另外这种结尾只见于load
  • si128/si256:未指定的128或者256位向量,不关心向量里到底是什么类型,反正是128bit/256bit

    例如:__m256i _mm_broadcastsi128_si256 (__m128i a)
函数 描述
load 用于从内存加载到寄存器
set 用于从内存加载到寄存器
add、sub、mul、div 加减乘除
store 用于从寄存器写入到内存
cast 数据类型转换
compare 数据比较
logical(and|or|test) 逻辑运算
mask 条件判断

一个小demo

#include <iostream>
#include <immintrin.h>
int main(int argc, char* agv[])
{
__m256 a = _mm256_set_ps(8.0, 7.0, 6.0,5.0,
4.0, 3.0, 2.0, 1.0);
__m256 b = _mm256_set_ps(18.0, 17.0, 16.0, 15.0,
14.0, 13.0, 12.0, 11.0);
__m256 c = _mm256_add_ps(a, b); float d[8];
_mm256_storeu_ps(d, c); std::cout << "result equals:" << d[0] <<","<<d[1]<<","<<d[2]<<","
<<d[4]<<","<<d[5]<<","<<d[6]<<","<<d[7]<<std::endl;
return 0;
}

编译时,需要增加编译参数-msse -mavx -mavx2

g++ simple_demo.cc -mavx2 -mavx -msse

运行结果:

result equals:12,14,16,20,22,24,26

PS:

编译参数 -mavx 是用于启用 AVX(Advanced Vector Extensions)指令集的选项。

AVX 是 Intel 推出的一种 SIMD(Single Instruction, Multiple Data)扩展指令集,它可以提高向量化计算的性能。

在使用 g++ 编译器时,可以使用 -mavx 参数来启用 AVX 指令集的支持

需要注意的是,使用 -mavx 参数编译的程序只能在支持 AVX 指令集的 CPU 上运行,否则可能会导致程序崩溃或产生错误。因此,在使用 -mavx 参数之前,建议先检查目标机器是否支持 AVX 指令集。

支持的函数操作:

存取操作(load/store/set)

set操作类型 描述
_mm256_setzero_ps/pd 返回一个全为0的float类型的向量
_mm256_setzero_si256 返回一个全为0的整型向量
_mm256_set1_ps/pd 返回一个float类型的数填充向量
_mm256_set1_epi8/epi16/epi32/epi64x 用整型数填充向量
_mm256_set_ps/pd 用8个float或4个double类型数字初始化向量
_mm256_set_epi8/epi16/epi32/epi64x 用一个整型数初始化向量
_mm256_set_m128/m128d/m128i 用2个128位的向量初始化一个256位向量
_mm256_setr_ps/pd 用8个float或4个double的转置顺序初始化向量
_mm256_setr_epi8/epi16/epi32/epi64x 用若干个整型的转置顺序初始化向量
load操作类型 描述
_mm256_load_ps/pd 从对齐的内存地址加载浮点向量
_mm256_load_si256 从对齐的内存地址加载整型向量
_mm256_loadu_ps/pd 从未对齐的内存地址加载浮点向量
_mm256_loadu_si256 从未对齐的内存地址加载整型向量
_mm_maskload_ps/pd 根据掩码加载128位浮点向量的部分
_mm256_mask_ps/pd 根据掩码加载256位浮点向量的部分
(2)_mm_maskload_epi32/64 根据掩码加载128位整型向量的部分
(2)_mm256_maskload_epi32/64 根据掩码加载256位整型向量的部分

PS:

最后两个函数前面有个(2),代表只在AVX2中支持

store函数:

算术运算

  • 加减法
数据类型 描述
_mm256_add_ps/pd 对两个浮点向量做加法
_mm256_sub_ps/pd 对两个浮点向量做减法
(2)_mm256_add_epi8/16/32/64 对两个整形向量做加法
(2)_mm256_sub_epi8/16/32/64 对两个整形向量做减法
(2)_mm256_adds_epi8/16 (2)_mm256_adds_epu8/16 两个整数向量相加且考虑内存饱和问题
(2)_mm256_subs_epi8/16 (2)_mm256_subs_epu8/16 两个整数向量相减且考虑内存饱和问题
_mm256_hadd_ps/pd 水平方向上对两个float类型向量做加法
_mm256_hsub_ps/pd 垂直方向上最两个float类型向量做减法
(2)_mm256_hadd_epi16/32 水平方向上对两个整形向量做加法
(2)_mm256_hsub_epi16/32 水平方向上最两个整形向量做减法
(2)_mm256_hadds_epi16 对两个包含short类型的向量做加法且考虑内存饱和的问题
(2)_mm256_hsubs_epi16 对两个包含short类型的向量做减法且考虑内存饱和的问题
_mm256_addsub_ps/pd 加上和减去两个float类型的向量

将饱和度考虑在内的函数将结果钳制到可以存储的最小/最大值。没有饱和的函数在饱和发生时忽略内存问题。

而在水平方向上做减法的意思如下图:



_mm256_hadd_pd接口描述

Operation
dst[63:0] := a[127:64] + a[63:0]
dst[127:64] := b[127:64] + b[63:0]
dst[191:128] := a[255:192] + a[191:128]
dst[255:192] := b[255:192] + b[191:128]
dst[MAX:256] := 0

最后一个指令:_mm256_addsub_ps/pd 在偶数位置减去,奇数位置加上,获最后的目标向量

  • 乘除法
数据类型 描述
_mm256_mul_ps/pd 对两个float类型的向量进行相乘
(2)_mm256_mul_epi32 (2)_mm256_mul_epu32 将包含32位整数的向量的最低四个元素相乘
(2)_mm256_mullo_epi16/32 Multiply integers and store low halves
(2)_mm256_mulhi_epi16 (2)_mm256_mulhi_epu16 Multiply integers and store high halves
(2)_mm256_mulhrs_epi16 Multiply 16-bit elements to form 32-bit elements
_mm256_div_ps/pd 对两个float类型的向量进行想除

  • 融合乘法和加法
数据类型 描述
(2)_mm_fmadd_ps/pd/ (2)_mm256_fmadd_ps/pd 将两个向量相乘,再将积加上第三个。(res=a*b+c)
(2)_mm_fmsub_ps/pd/ (2)_mm256_fmsub_ps/pd 将两个向量相乘,然后从乘积中减去一个向量。(res=a*b-c)
(2)_mm_fmadd_ss/sd 将向量中最低的元素相乘并相加(res[0]=a[0]*b[0]+c[0])
(2)_mm_fmsub_ss/sd 将向量中最低的元素相乘并相减(res[0]=a[0]*b[0]-c[0])
(2)_mm_fnmadd_ps/pd (2)_mm256_fnmadd_ps/pd 将两个向量相乘,并将负积加到第三个。(res = -(a * b) + c)
(2)_mm_fnmsub_ps/pd/ (2)_mm256_fnmsub_ps/pd 将两个向量相乘,并将负积加到第三个 (res = -(a * b) - c)
(2)_mm_fnmadd_ss/sd 将两个向量的低位相乘,并将负积加到第三个向量的低位。(res[0] = -(a[0] * b[0]) + c[0])
(2)_mm_fnmsub_ss/sd 将最低的元素相乘,并从求反的积中减去第三个向量的最低元素。(res[0] = -(a[0] * b[0]) - c[0])
(2)_mm_fmaddsub_ps/pd/ (2)_mm256_fmaddsub_ps/pd 将两个矢量相乘,然后从乘积中交替加上和减去(res=a*b+/-c)
(2)_mm_fmsubadd_ps/pd/ (2)_mmf256_fmsubadd_ps/pd 将两个向量相乘,然后从乘积中交替地进行减法和加法(res=a*b-/+c)(奇数次方,偶数次方)
  • 排列和洗牌(Permuting and Shuffling)

    排列 Permuting
数据类型 描述
_mm_permute_ps/pd _mm256_permute_ps/pd 根据8位控制值从输入向量中选择元素
(2)_mm256_permute4x64_pd/ (2)_mm256_permute4x64_epi64 根据8位控制值从输入向量中选择64位元素
_mm256_permute2f128_ps/pd 基于8位控制值从两个输入向量中选择128位块
_mm256_permute2f128_si256 基于8位控制值从两个输入向量中选择128位块
_mm_permutevar_ps/pd _mm256_permutevar_ps/pd 根据整数向量中的位从输入向量中选择元素
(2)_mm256_permutevar8x32_ps (2)_mm256_permutevar8x32_epi32 使用整数向量中的索引选择32位元素(浮点和整数)

洗牌Shuffling

数据类型 描述
_mm256_shuffle_ps/pd 根据8位值选择浮点元素
_mm256_shuffle_epi8/ _mm256_shuffle_epi32 根据8位值选择整数元素
(2)_mm256_shufflelo_epi16/ (2)_mm256_shufflehi_epi16 基于8位控制值从两个输入向量中选择128位块

对于_mm256_shuffle_pd,只使用控制值的高4位。如果输入向量包含int或float,则使用所有控制位。对于_mm256_shuffle_ps,前两对位从第一个矢量中选择元素,第二对位从第二个矢量中选择元素。

举例:

#include <iostream>
#include <immintrin.h> void print_value(__m128 value)
{
float *v = (float *)&value;
std::cout<< v[0] << "," << v[1] << ","<< v[2] << ","<<v[3] << std::endl;
} // load,用于从内存加载到寄存器中
__m128 function_load(float const*mem_addr)
{
return _mm_load_ps(mem_addr);
} // set,用于从内存加载到寄存器中
__m128 function_set(float const *mem_addr)
{
return _mm_set_ps(mem_addr[3], mem_addr[2], mem_addr[1], mem_addr[0]);
} // 直接初始化
__m128 direct_init(float const *mem_addr)
{
__m128 value = {mem_addr[0], mem_addr[1], mem_addr[2], mem_addr[3] };
return value;
} int main()
{
float *mem_addr = (float*)malloc(sizeof(float)*4);
mem_addr[0] = 1.0f;
mem_addr[1] = 2.0f;
mem_addr[2] = 3.0f;
mem_addr[3] = 4.0f;
print_value(function_load(mem_addr));
print_value(function_set(mem_addr));
print_value(direct_init(mem_addr)); return 0;
}

运行结果:

一个简单的加法验证SIMD效率上的提升

// 写一个简单的程序来验证SIMD在效率方面的提升
#include <iostream>
#include <nmmintrin.h>
#include <immintrin.h>
#include <string>
#include <time.h>
#include <stdlib.h> void print_time_result(std::string func_name, clock_t start, clock_t finish, int res)
{
double time = (double(finish - start) / double(CLOCKS_PER_SEC)) * 1000; // ms
std::cout << func_name << ": " << time << "ms, result = " << res << std::endl;
} // 普通的加法
void normal_add(int *nums, size_t n)
{
clock_t start = clock();
int sum = 0;
for (size_t i = 0; i < n; i++)
{
sum += nums[i];
}
clock_t finish = clock();
print_time_result("normal_add", start, finish, sum);
} // 使用128bit的SIMD
void SSE_add(int *nums, size_t n)
{
clock_t start = clock(); __m128i simd_sum = _mm_setzero_si128();// 累加和
int normal_sum = 0; size_t loop = n / 4;
size_t reserve = n % 4; __m128i *p = (__m128i *)nums;
for (size_t i = 0; i < loop; i++)
{
simd_sum = _mm_add_epi32(simd_sum, *p);
p++;
} int *q = (int *)p;
for (size_t i = 0; i < reserve; i++)
{
normal_sum += q[i];
}
normal_sum += (((int *)&simd_sum)[0] + ((int *)&simd_sum)[1] + ((int *)&simd_sum)[2] + ((int *)&simd_sum)[3]); clock_t finish = clock();
print_time_result("SSE_add", start, finish, normal_sum);
} // 使用256bit的SIMD
void AVX2_add(int *nums, size_t n)
{
clock_t start = clock(); __m256i simd_sum = _mm256_setzero_si256();
__m256i simd_load;
int normal_sum = 0; size_t loop = n / 8;
size_t reserve = n % 8; __m256i *p = (__m256i *)nums;
for (size_t i = 0; i < loop; i++)
{
simd_load = _mm256_load_si256((const __m256i *)p);
simd_sum = _mm256_add_epi32(simd_sum, simd_load);
p++;
} int *q = (int *)p;
for (size_t i = 0; i < reserve; i++)
{
normal_sum += q[i];
}
normal_sum += (((int *)&simd_sum)[0] + ((int *)&simd_sum)[1] +
((int *)&simd_sum)[2] + ((int *)&simd_sum)[3] +
((int *)&simd_sum)[4] + ((int *)&simd_sum)[5] +
((int *)&simd_sum)[6] + ((int *)&simd_sum)[7]); clock_t finish = clock();
print_time_result("AVX2_add", start, finish, normal_sum);
} int main()
{
size_t n = 10000000;
int *nums = NULL;
// nums = (int *)malloc(sizeof(int) * n);
posix_memalign((void **)&nums, 32, sizeof(int) * n); // 注意要32字节对齐,否则会段错误 for (size_t i = 0; i < n; i++)
{
nums[i] = 5;
}
normal_add(nums, n);
SSE_add(nums, n);
AVX2_add(nums, n);
return 0;
}

上述代码其实就是在做10000000个5相加,normal_add()是按照一般思路,循环1千万次加法,SSE_add()是4个一组(128bit)相加,AVX2_add()是8个一组(256bit)相加。

注意:对于SSE,我们申请的空间要16字节对齐,对于AVX要32字节对齐,否则会出现段错误。

// 方法一:
__attribute__((aligned(16))) float A[10000000];// Linux下
// 方法二:
posix_memalign((void **)&B, 32, sizeof(int) * 10000000); // #include <stdlib.h>

运行结果:



也许和硬件不一样,在自己电脑上还是有提升的。

参考博客

https://blog.triplez.cn/avx-avx2-learning-notes/

https://blog.csdn.net/just_sort/article/details/94393506

https://zhuanlan.zhihu.com/p/457505686

Intel指令集及SIMD数据加速的更多相关文章

  1. SIMD数据并行(二)——多媒体SIMD扩展指令集

    在计算机体系中,数据并行有两种实现路径:MIMD(Multiple Instruction Multiple Data,多指令流多数据流)和SIMD(Single Instruction Multip ...

  2. 借助 SIMD 数据布局模板和数据预处理提高 SIMD 在动画中的使用效率

    原文链接 简介 为发挥 SIMD1 的最大作用,除了对其进行矢量化处理2外,我们还需作出其他努力.可以尝试为循环添加 #pragma omp simd3,查看编译器是否成功进行矢量化,如果性能有所提升 ...

  3. SIMD数据并行(三)——图形处理单元(GPU)

    在计算机体系中,数据并行有两种实现路径:MIMD(Multiple Instruction Multiple Data,多指令流多数据流)和SIMD(Single Instruction Multip ...

  4. SIMD数据并行(四)——三种结构的比较

    在计算机体系中,数据并行有两种实现路径:MIMD(Multiple Instruction Multiple Data,多指令流多数据流)和SIMD(Single Instruction Multip ...

  5. Intel指令集专有词解释

    SSE 概述 SSE(Streaming SIMD Extensions)是英特尔在AMD的3D Now!发布一年之后,在其计算机芯片Pentium III中引入的指令集,是MMX的超集.AMD后来在 ...

  6. 验证 .NET 4.6 的 SIMD 硬件加速支持的重要性

    SIMD 的意思是 Single Instruction Multiple Data.顾名思义,一个指令可以处理多个数据. .NET Framework 4.6 推出的 Nuget 程序包 Syste ...

  7. SIMD数据并行(一)——向量体系结构

    在计算机体系中,数据并行有两种实现路径:MIMD(Multiple Instruction Multiple Data,多指令流多数据流)和SIMD(Single Instruction Multip ...

  8. 【转】安装Intel HAXM为Android 模拟器加速,30秒内启动完成

    http://www.cnblogs.com/Li-Cheng/p/4351966.html http://www.cnblogs.com/csulennon/p/4178404.html https ...

  9. 安装Intel HAXM为Android 模拟器加速,30秒内启动完成

    要求 必备知识 windows 7 基本操作. 运行环境 windows 7(64位); Android Studio 1.1.0;JDK 1.7.0_75(64位);android-sdk_r24 ...

  10. 使用 Intel HAXM 为 Android 模拟器加速,媲美真机(转)

    源:http://www.cnblogs.com/beginor/archive/2013/01/13/2858228.html 慢的问题. Intel HAXM (Hardware Accelera ...

随机推荐

  1. idea debug时提示”Method breakpoints may dramatically slow down debugging“的解决办法

  2. 关于我在使用Steamlit中碰到的问题及解决方案总结

    Steamlit 并不支持一个可以预览本地文件的路径选择器(并不上传文件) 解决方案:使用 Python 自带的 tkinter 来完成 参考:[Streamlit 选择文件夹的曲折方案]Stream ...

  3. AI技术革命,工作效率10个最佳AI工具

    AI技术革命,工作效率10个最佳AI工具 目       录 1.      Vidwud AI PowerPoint生成器... 4 2.      Vidnoz AI音乐视频生成器... 5 3. ...

  4. pandas 删除指定条件的行

    inplace=True:不创建新的对象,直接对原始对象进行修改: inplace=False:对数据进行修改,创建并返回新的对象承载其修改结果. 删除工作日餐补为0的记录 row_index=df[ ...

  5. Netty基础—2.网络编程基础二

    大纲 1.网络编程简介 2.BIO网络编程 3.AIO网络编程 4.NIO网络编程之Buffer 5.NIO网络编程之实战 6.NIO网络编程之Reactor模式 1.网络编程简介 既然是通信,那么肯 ...

  6. 使用 HBuilderX 轻松解决 CSS 代码在一行的问题

    前言 最近在做博客园的界面美化,用的是园内大佬的开源项目,配置超级简单,只需要复制粘贴代码就好啦. 但在粘贴 CSS 代码时遇到一个问题,那就是所有代码都挤在了一行,没有一点排板的样子(如下图),对我 ...

  7. C#语言碎片:Switch-Case语句字符串匹配

    Switch case语句在处理字符串类型匹配时候,case条件需要设置为静态常量或者一个具体的字符串: 因为工具类ToolHand.Name 为变量,所以编译不通过. 使用if语句来逐个判断: 看A ...

  8. go 数组或切片转为字符串

    数组转字符串 将 var slice = []int{1, 2, 3, 4},转为 [1,2,3,4] 可以使用 json.Marshal(),这是因为 json 包,会将数组和切片值编码为JSON数 ...

  9. php nginx 504 Gateway Timeout 网关超时错误

    对于 Nginx + FastCGI上的504网关超时错误(php-fpm),我们可以修改PHP和nginx的执行超时时间. 配置php 修改 php.ini(CentOS路径是/etc/php.in ...

  10. 面试题-MySQL和Redis(更新版)

    前言 MySQL和Redis部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,并添加了一些比较重要的问题,希望对大家起到 ...