编译优化之 - 向量化优化入门

article/2025/9/12 22:22:53

1. 介绍
2. Intel高级向量扩展
3. GCC中向量化
4. ICC中向量化
5. AOCC/LLVM中向量化

1. 介绍

什么是自动向量化?

  自动向量化(automatic vectorization)是自动并行化(automatic parallelization)的一种特殊情况,它将一次处理一对的标量运算转换为一次并行处理多对的向量运算。因此向量化可以显着加速一些带循环的程序运算,尤其是在大型数据集上。根据arch信息,编译器优化的目标可以是Intel或AMD处理器中的SSE*、AVX/AVX2或更高级的指令,或ARM处理器中的NEON指令。
  默认情况下,GCC、ICC或AOCC/LLVM编译器中都启用了部分自动向量化功能。这些指令受运行时检查保护,即运行时检查机器是否支持该指令集,如果不支持则使用其它的指令集。

什么是SIMD指令?

  SIMD(Single instruction, multiple data)单指令多数据技术能对程序中数据进行并行处理,提高吞吐量。它将原来需要多次装载的数据一次性装载到向量寄存器,即SIMD指令允许在一个步骤中处理多个数据。现代的CPU设计都包括SIMD指令,以提高多媒体程序和科学计算程序的性能,但SIMD与利用线程的SIMT不同。SIMD指令的首次使用是在1966年,它一般分为两种:

  • 手工向量化:通过内嵌汇编码或编译器提供的内函数来添加SIMD指令。
  • 自动向量化:利用编译器分析串行程序中控制流和数据流的特征,识别程序中可以向量执行的部分,将标量语句自动转换为相应的SIMD向量语句。

机器支持哪些指令集?

cat /proc/cpuinfo

本机cpu支持的simd指令集如下:
cpu

gcc -c -Q -march=native --help=target

本机GCC8.2支持的simd指令集如下:
gcc

注意:向量化的操作需要机器硬件的支持!


2. Intel高级向量扩展

较早的相关英特尔SSE指令还支持各种有符号和无符号整数大小,包括有符号和无符号byte(B,8位),word(W,16位),doubleword(DW,32位),quadword( QW(64位)和doublequadword(DQ,128位)长度。

表1:英特尔AVX后缀标记

标记含义
[s / d]单精度或双精度浮点
[ps / pd / sd]打包单精度、打包双精度、标量双精度
[i / u] nnn位大小为nnn的有符号或无符号整数,其中nnn是128、64、32、16或8
epi32扩展打包的32位有符号整数
si256标量256位整数

更多关于Intel Advanced Vector Extensions的信息请见:Introduction to Intel® Advanced Vector Extensions

  1. MMX指令
    MultiMedia eXtensions(MMX),MMX指令主要使用的寄存器为MM0 ~ MM7,与浮点运算不能同时进行。MMX指令能一次性地操作1个64-bit的数据、或者两个32-bit的数据、或者4个16-bit的数据、或者8个8-bit的数据。
    MMX指令集的扩展包括:3DNow!、SSE、AVX

  2. SSE指令
    Streaming SIMD eXtensions(SSE),SSE指令采用了独立的寄存器组XMM0 ~ XMM7,64位模式下为XMM0 ~ XMM15,并且这些寄存器的长度也增加到了128-bit。
    SSE指令的升级版包括:SSE2/SSE3/SSSE3/SSE4

  3. AVX/AVX2指令
    Advanced Vector eXtentions(AVX),AVX对XMM寄存器做了扩展,从原来的128-bit扩展到了256-bit,并从XMM0–XMM7重命名为YMM0–YMM7,仍可通过SSE指令对YMM寄存器的低128位进行操作。新指令使用英特尔所谓的VEX前缀进行编码,这是一个两字节或三字节的前缀,旨在消除当前和将来的x86/x64指令编码的复杂性。AVX2将大多数整数命令扩展为256位,并引入了融合的乘加(FMA)操作。

  4. FMA指令
    Fused-Multiply-Add(FMA),FMA指令集是128-bit和256-bit的SSE的扩展指令集,以进行乘加运算。共有两种变体:FMA4、FMA3,自2014年以来,从PILEDRIVER架构开始,AMD处理器就支持FMA3;从Haswell处理器和Broadwell处理器开始,英特尔则支持FMA3。

  5. AVX512*指令
    英特尔架构处理器支持旧式和现代指令集,从64位MMX扩展到新的512位指令AVX-512。ZMM的低256-bit与YMM混用。ZMM的前缀为EVEX。与AVX / AVX2相比,AVX-512最显着的新功能是512位矢量寄存器宽度。

  6. 其他指令
    KNC等其他指令集。

Intel不同架构向量化指令集如下:

intel

表2: 部分FMA指令说明

指令含义
VFMADD[z][P/S][D/S]乘法加法融合指令:A = r1 * r2 + r3(packed/scalar of double/single)
VFMSUB[z][P/S][D/S]乘法减法融合指令:A = r1 * r2-r3(packed/scalar double/single)
VFNMADD[z][P/S][D/S]乘法加法融合的负指令:A = -r1 * r2+r3( packed/scalar double/single)
VFNMSUB[z][P/S][D/S]乘法减法融合的负指令: A = -r1 * r2-r3(packed/scalar double/single)
VFMADDSUB[z]P[D/S]乘法与加减法交替的融合指令,奇数索引:A = r1 * r2 + r3,偶数索引:A = r1 * r2-r3(packed double/single)
VFMSUBADD[z]P[D/S]乘法与加减法交替的融合指令,奇数索引:A = r1 * r2-r3 ,偶数索引:A = r1 * r2+r3(packed double/single)

其中[z]代表字符串132或213或231,对应操作数A,B,C的使用顺序:

  • 132是A = AC + B
  • 213是A = AB + C
  • 231是A = BC + A

3. GCC中向量化

GCC8.2.0中关于向量化操作的选项有:-ftree-loop-vectorize、-ftree-slp-vectorize、-ftree-loop-if-convert、-ftree-vectorize、-fvect-cost-model=model、-fsimd-cost-model=model。前两个向量化选项默认情况下在-O3中已启用,这里不一一说明。

具体每个选项的使用及详细介绍请见:https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Optimize-Options.html#Optimize-Options

3.1 示例

使用的示例代码如下:

// vect.c
int main()
{int N = 1000;int a[N], b[N], c[N];for (int i = 0; i < N; i++) {b[i]=c[i]=i;a[i] = b[i] + c[i];}for (int i = 0; i < N; i++) {a[i] = a[i] + a[i];}printf("%d\n",a[100]);return 0;
}

如何知道一个循环是否启用了向量化?  gcc中可使用-fopt-info-vec-optimized-fopt-info-vec命令来查看,操作如下:
gcc1
或使用-fopt-info-vec-all选项记录下所有信息,并保存到一个文件,方便查看,如下所示:
gcc2
GCC中向量化操作功能有较多限制,比如:

  • 循环中有较多条件语句,函数调用等,复杂的cfg。这样做不了向量化优化
  • 嵌套循环中,外层循环的索引参与内部循环计算,导致无法向量化优化
  • 其他一些特性

假若想显式的指定gcc采用什么向量化进行编译(前提是硬件支持),可参考如下操作:

gcc -O3 -march=core-avx2 vect.c -std=c99 -S -o vect.s

使用-march=core-avx2指令指定采用AVX2指令集(可指定其他指令集)。部分汇编码如下:
gcc3


4. ICC中向量化

Intel编译器中可用一下几种方式生成向量化指令:

  • SIMD vector intrinsics
  • OpenMP* 4.0 (SIMD part)
  • Auto-vectorization
  • Inline assembly code

英特尔MIC架构向量化的最重要方面之一是数据对齐。例如向量加法操作,如果编译器不知道数据对齐方式(通常是这种情况是对齐方式不正确),则将迫使编译器生成效率较低的代码。因此,重要的是优化数据的对齐并适当地将信息传达给编译器。

更多关于循环矢量化优化程序的信息请见:https://software.intel.com/en-us/articles/program-optimization-through-loop-vectorization

4.1 示例

在此使用的icc18,和测试代码如下:

// tmp.c
#include<stdio.h>
int main()
{int n = 1000;int a[n],b[n];for (int i = 0; i < n; ++i){a[i] = i * 10;b[i] = a[i]+i * 10;}printf("%d\n",b[10]);return 0;
}

如何知道一个循环是否启用了向量化?  icc中可使用-qopt-report -qopt-report-phase=vec命令查看,操作如下:

icc -qopt-report -qopt-report-phase=vec -O3 tmp.c -std=c99// icc: remark #10397: optimization reports are generated in *.optrpt files in the output locationvim tmp.optrpt// 报告结果如下
Intel(R) Advisor can now assist with vectorization and show optimizationreport messages with your source code.
See "https://software.intel.com/en-us/intel-advisor-xe" for details.Begin optimization report for: main()Report from: Vector optimizations [vec]LOOP BEGIN at tmp.c(6,4)remark #15300: LOOP WAS VECTORIZED
LOOP ENDLOOP BEGIN at tmp.c(6,4)
<Remainder loop for vectorization>remark #15301: REMAINDER LOOP WAS VECTORIZED
LOOP END
===========================================================================

如上remark #15301: REMAINDER LOOP WAS VECTORIZED所示,icc对该代码启用了循环优化,部分汇编码如下所示:
icc1
如果要求编译器不要向量化特定循环,可以使用#pragma novector命令,如下:

#pragma novector 
for (int i = 0; i < n; ++i)//同样使用以上操作,生成报告如下
LOOP BEGIN at tmp.c(7,4)remark #15319: loop was not vectorized: novector directive used
LOOP END
===========================================================================

部分汇编码如下所示:
icc2

类似命令还有:

  • #pragma vector always
  • #pragma vector align

假若想显式的指定icc采用什么向量化进行编译(前提是硬件支持),可参考如下操作:

icc -qopt-report -qopt-report-phase=vec -O3 -march=core-avx2 tmp.c -S -o tmp0.s -std=c99

使用-march=core-avx2指令指定采用AVX2指令集(可指定其他指令集),-qopt-report -qopt-report-phase=vec记录向量化信息。部分汇编码如下:
icc3


5. AOCC/LLVM中向量化

  LLVM有两种向量化:作用于循环的Loop Vectorizer、将代码中找到的多个标量合并为矢量的SLP Vectorizer。默认情况下已启用了两种向量化优化。
  AOCC2.1中向量化相关的选项有:-enable-strided-vectorization、-enable-epilog-vectorization、-vectorize-memory-aggressively、-global-vectorize-slp、-region-vectorize、-suppress-fmas。

具体每个选项的使用及详细介绍请见:https://developer.amd.com/wp-content/resources/AOCC-2.1-Clang-the%20C%20C++%20Compiler.pdf

5.1 示例

LLVM中若想禁用Loop Vectorizer和SLP Vectorizer,可以使用命令行标志通过clang禁用它:

clang ... -fno-vectorize  ohter.c
clang ··· -fno-slp-vectorize file.c

可以使用命令行标志“ -force-vector-width”来控制矢量化SIMD宽度:

clang  -mllvm -force-vector-width=8 ...
opt -loop-vectorize -force-vector-width=8 ...

可以使用命令行标志“ -force-vector-interleave”来控制展开因子:

clang  -mllvm -force-vector-interleave=2 ...
opt -loop-vectorize -force-vector-interleave=2 ...

以上示例及说明参考自“Auto-Vectorization in LLVM”,更多详细信息请见:https://llvm.org/docs/Vectorizers.html

在此使用以下示例代码:

// tmp.c
#include<stdio.h>
int main()
{int n = 1000;int a[n],b[n];for (int i = 0; i < n; ++i){a[i] = i * 2;for(int j = 0; j<n/2;j++){b[i] = a[i]+a[j]*2;}}printf("%d\n",b[10]);return 0;
}

如何知道一个循环是否启用了向量化?  llvm中可使用-Rpass-missed=loop-vectorize命令查看,操作如下:

clang -O3 -Rpass-missed=loop-vectorize tmp.c// 得到信息如下。可知对于如上循环很多编译器是做不了向量化优化的
tmp.c:6:4: remark: loop not vectorized [-Rpass-missed=loop-vectorize]for (int i = 0; i < n; ++i)^

假如使用一下代码:

#include<stdio.h>
int main()
{int n = 1000;int a[n],b[n];for (int i = 0; i < n; ++i){a[i] = i * 2;}printf("%d\n",a[10]);return 0;
}

再使用clang -O3 -Rpass=loop-vectorize tmp.c命令查看,便得到一下结果:

tmp.c:6:4: remark: vectorized loop (vectorization width: 4, interleaved count: 2) [-Rpass=loop-vectorize]for (int i = 0; i < n; ++i)^

对于以上代码,AOCC2.1使用-Wl,-mllvm -Wl,-region-vectorize命令操作如下:

clang -O3 -Rpass=loop-vectorize tmp.c -flto -Wl,-mllvm -Wl,-region-vectorize

AOCC2.1中向量化操作与LLVM基本相似,在此不做更多介绍。


References:

  • SIMD自动向量化编译优化概述
  • https://colfaxresearch.com/skl-avx512/
  • https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
  • https://llvm.org/docs/Vectorizers.html
  • https://developer.amd.com/wp-content/resources/AOCC-2.1-Clang-the%20C%20C++%20Compiler.pdf

http://chatgpt.dhexx.cn/article/GPk0R5Al.shtml

相关文章

数据库向量化如何进行性能优化

数据库向量化如何进行性能优化 前面提到&#xff0c;数据库向量化是一个巨大的、系统的性能优化工程&#xff0c;两年来&#xff0c;我们实现了数百个大大小小的优化点。我将 StarRocks 向量化两年多的性能优化经验总结为 7 个方面 &#xff08;注意&#xff0c;由于向量化执行…

simulink 状态空间加反馈报错

状态空间模型&#xff08;可控&#xff09;通过状态反馈或输出反馈可以自由配置极点和特征向量&#xff0c;得到理想的运动状态&#xff0c;通过计算得到的反馈增益矩阵K便可构建simulink模型&#xff0c;但常常报错&#xff0c;原因如下&#xff1a; 上图展示的是simulink模型…

状态空间方程MATLAB语句

1.连续系统 &#xff08;1&#xff09;使用系数矩阵获得传递函数 [num,den]ss2tf(A,B,C,D); &#xff08;2&#xff09;将传递函数写成因式分解&#xff08;零极点&#xff09;形式 [z,p,k]ss2zp(A,B,C,D) 或者 [z,p,k]tf2zp(num,den) &#xff08;3&#xff09;将给定形式…

基于matlab的系统状态空间转化

前段时间学习了一些关于通过系统状态方程判断系统可控性和可观测性&#xff0c;并由此求出其传递函数&#xff0c;基于传递函数判断其稳定性的一些知识。 一、常用的数学模型转换函数&#xff1a; 常用数学模型转换函数 ss2tf 将系统状态空间…

连续状态空间模型离散化

对于某状态空间模型&#xff1a; 其中&#xff1a; 将该连续模型离散化&#xff1a;&#xff08;代码如下&#xff09; clc;clear;close all A[-11.6028 7.1632 ;6.4909 -27.837 ]; B[3.086;5.4458]; [F,G]c2d(A,B,0.02) %0.02为采样周期 运行结果如下&#xff1a;&#xff0…

基于状态空间的数字控制器的设计

目录 一&#xff0e;目的... 2 二&#xff0e;内容... 2 三&#xff0e;实验过程与结果... 2 1&#xff0e;推算出控制对象的模型参数... 2 2. 状态空间的数字控制器的设计原理... 2 3、实验代码&#xff08;借鉴PPT内容&#xff09;... 3 4、仿真图像... 3 5、仿真结果…

人工智能:状态空间图(超详细经典例题讲解,通过例题教会你如何解决状态空间图问题)

1、定义 状态空间常记为三元组&#xff1a;<S&#xff0c;F&#xff0c;G>。 其中&#xff0c;S为初始状态的集合&#xff0c;F为操作的集合&#xff0c;G为目标状态的集合。 问题的状态空间图是一个描述该问题全部可能的状态及相互关系的有向图。 如考虑操作的代价&…

状态空间模型

&#xfeff;&#xfeff; 一、状态空间模型简述 状态空间模型是动态时域模型&#xff0c;以隐含着的时间为自变量。状态空间模型包括两个模型&#xff1a; 一是状态方程模型&#xff0c;反映动态系统在输入变量作用下在某时刻所转移到的状态&#xff1b; 二是输出或量测方程模…

什么是状态空间法

1 状态空间法 在经典控制理论中&#xff0c;在建立数学模型时是通过传递函数进行的&#xff0c;在这个过程中&#xff0c;只考虑输入和输出之间的关系&#xff0c;所以会将系统变成一个黑盒子&#xff0c;里面的内容被浓缩了。 而在现代控制理论中&#xff0c;会首先从系统中抽…

动态系统建模-状态空间方程

动态系统建模-状态空间方程 状态空间方程是现代控制理论的基础, 它以矩阵的形式表达系统状态变量、 输入及输出之间的关系。 它可以描述和处理多输入多输出(MultipleInput Multiple Output, MIMO) 的系统。 状态空间方程 单输入单输出(SingleInput Single Output,SISO) 系统…

人工智能——状态空间表示法

状态空间表示法 状态空间表示法引入问题状态空间的构成状态算符状态空间问题的解 状态空间法表示问题的步骤状态空间方法表示问题的步骤如下 利用状态空间求解问题的过程利用状态空间表示法解题示例状态空间表示法简要小结 状态空间表示法引入 状态空间表示法就是以 “ 状态空间…

传递函数与状态空间

传递函数与状态空间之间可相互转换&#xff0c;可以使用的matlab函数有 [A,B,C,D] tf2ss(NUM,DEN) [NUM,DEN] ss2tf(A,B,C,D,iu)传递函数的形式唯一&#xff0c;但状态空间的形式不唯一&#xff0c;可以有多种。 1、一阶惯性环节 时间常数为T&#xff0c;本身为低通滤波器&…

状态空间搜索

http://www.lencomputer.com/xk2008/lesson19/search_algorithm.htm 状态空间搜索是程序设计中的最基本方法之一。它通过在状态空间中的初始状态出发&#xff0c;按照一定的顺序和条件对空间中的状态进行遍历&#xff0c;最终找到目标状态。一般的状态空间搜索方法有枚举、深度…

状态空间

1. 定义 状态变量(state variables)是指在系统中所含变量个数最少的变量&#xff0c;也就是决定系统状态的最小数目的变量的有序集合&#xff0c;有时也称为状态向量&#xff08;state vector&#xff09;&#xff0c;例如表示天体运动状态的位置和速度的变量。状态变量表示系统…

状态空间树

状态空间树&#xff1a; 就是问题的解空间树&#xff0c;分为子集树和排列树 子集树 当所给的问题是从n个元素组成的集合set中找到满足某一条件的一个子集时&#xff0c;相应的解空间树称为子集。 要注意&#xff0c;这个解空间树是一个虚拟的树&#xff0c;并不是构建出来的…

matlab状态空间程序,将状态空间表示形式转换为传递函数

理想的一维振荡系统由位于两面墙壁间的两个单位质点 m1 和 m2 组成。每个质点通过一根单位弹性常量弹簧连接到最近的墙壁。另外一根弹簧连接这两个质点。传感器以 Fs=16 Hz 的频率对 a1 和 a2(质点的加速度)采样。 将总测量时间指定为 16 秒。定义采样间隔 Δt=1/Fs。 Fs = 16;…

状态空间表示

前言 本文隶属于专栏《人工智能》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见人工智能 引子 人工智能的多个研究领域从求解现实问题的过程来看&#xf…

现控笔记(二):状态空间表达式

控制系统状态空间表达式 系统动态过程的两类数学描述&#xff1a; 外部描述&#xff1a;&#xff08;输入——输出描述&#xff09; 内部描述&#xff1a;状态空间描述 两个方程描述&#xff1a;状态方程&#xff08;动态&#xff09;&#xff0c;输出方程&#xff08;静态&am…

【现控】系统状态空间表达式

【现控】1 系统状态空间表达式 一、基本概念 状态&#xff1a;状态是变化的&#xff0c;是时域里的一系列变量。它可以数字、曲线或者其他什么更为抽象的东西描述。 状态变量&#xff1a;能够完全描述系统的最小一组变量。可抽象可具体。 状态空间&#xff1a;以状态变量构成…

现代控制理论——状态、状态空间、状态空间描述

一、状态&#xff1a; 动态系统的状态粗略地说就是指系统的过去、现在和将来的运动状况。精确地说&#xff0c;状态需要一组必要而充分的数据说明。 对于运动的小车&#xff0c;系统的状态可以为位置和速度&#xff0c;对于电机可以为转速。 二、系统变量 1、状态变量 系统…