AVX简介

SIMD

SIMD(Single Instruction Multiple Data,单指令多数据流),是一种实现空间上的并行性的技术。这种技术使用一个控制器控制多个处理单元,同时对一组数据中的每一个数据执行相同的操作。在 SIMD 指令执行期间,任意时刻都只有一个进程在运行,即 SIMD 没有并发性,仅仅只是同时进行计算。

在 Intel 的 x86 微架构处理器中,SIMD 指令集有 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AVX、AVX2、AVX512。

AVX

AVX 是 SSE 架构的延伸,将 SSE 的 XMM 128bit 寄存器升级成了 YMM 256bit 寄存器,同时浮点运算命令扩展至 256 位,运算效率提升了一倍。另外,AVX 还添加了三操作数指令,以减少在编码时先复制再运算的动作。

AVX2 将大多数整数运算命令扩展至 256 位,同时支持 FMA(Fused Multiply-Accumulate,融合乘法累加)运算,可以在提高运算效率的同时减少运算时的精度损失。

AVX512 将 AVX 指令进一步扩展至 512 位。

AVX指令介绍

参考该网站:Crunching Numbers with AVX and AVX2 - CodeProject

基于AVX的矩阵乘法

算法思想

结合上图,首先需要将矩阵 C 初始化为 0 矩阵。为了充分利用 AVX 单指令多数据的计算优势,需要以向量为基本运算单元,将矩阵 A 第 i 行第 j 列的元素与矩阵 B 第 j 行的向量相乘,结果向量与矩阵 C 第 i 行的向量累加。当矩阵 A 第 i 行的所有元素均与矩阵 B 每一行的向量相乘并累加至矩阵 C 第 i 行的向量后,矩阵 C 第 i 行的计算结束。

假设矩阵的元素为 float 类型(32位)的浮点数,由于 AVX 采用 256 位寄存器存储向量,所以一个向量可以存储 8 个元素。当矩阵的列数不为 8 的整数倍时,即一行的元素个数不为 8 的整数倍时,最后一个向量的后几位需要设置为 0。若采用兼容未对齐数据的方法则需要解决内存冲突的问题。

代码

在具体实现时,矩阵 A 第 i 行第 j 列的数据 A[i][j] 会被复制成长度为 8 的向量 vecA,矩阵 B 每一行的向量 vecB 则从指定内存地址读入。由于矩阵的列数可以不是 8 的整数倍(对于元素为float类型的矩阵),所以数据可以是未对齐的,因此采用 loadu 从内存装入矩阵数据。

对于向量的相乘和累加,因为两个 n 位数字相乘的结果最大可以占 2n 位,因此当两个 float 类型的浮点数 a 与 b 相乘时,其结果是最接近 a*b 的 float 数值 round(a*b)。在本算法中,两个浮点数相乘后还要累加到 C 矩阵中,相比于普通的相乘后累加 round(a*b)+c,FMA运算可以实现 round(a*b+c)。因此这种方式拥有更高的精确性。

这里我并没有将最后一个向量的后几位设置为 0,所以存在一个问题:假如此时矩阵 B 为 7*6,当 loadu 读入矩阵 B 第二行的数据时,第二行只有 6 个 float 类型的 32 位浮点数,不足以构成 256 位的向量,因此指令会继续读入第三行的前两个数,计算完成后,第三行的前两个数也被保存至矩阵 C。接着,指令读入第三行和第四行前两个数,计算完成后保存。不难发现,从第二行开始到最后一行,每行的前两个数都被计算了两次。并且对于第一次计算来说,后两个数的计算过程不符合算法的原理,是错误数据。解决方法很简单,在矩阵 C 各行开始计算之前,重新初始化该行的元素值为 0,以消除第一次错误计算的影响。


#include <immintrin.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#define MATRIX_SIZE 8192
#define NUM_THREAD 4
float matA[MATRIX_SIZE][MATRIX_SIZE];
float matB[MATRIX_SIZE][MATRIX_SIZE];
float matC[MATRIX_SIZE][MATRIX_SIZE];
int step = 0;
void* multiplicationAVX(void* args) {
__m256 vecA, vecB, vecC;
int thread = step++;
for (int i = thread * MATRIX_SIZE / NUM_THREAD;
i < (thread + 1) * MATRIX_SIZE / NUM_THREAD; i++) {
for (int j = 0; j < MATRIX_SIZE; j++) {
matC[i][j] = 0.0f;
}
for (int j = 0; j < MATRIX_SIZE; j++) {
vecA = _mm256_set1_ps(matA[i][j]);
for (int k = 0; k < MATRIX_SIZE; k += 8) {
vecB = _mm256_loadu_ps(&matB[j][k]);
vecC = _mm256_loadu_ps(&matC[i][k]);
vecC = _mm256_fmadd_ps(vecA, vecB, vecC);
_mm256_storeu_ps(&matC[i][k], vecC);
}
}
}
return 0;
}
void* multiplicationNormal(void* args) {
int thread = step++;
for (int i = thread * MATRIX_SIZE / NUM_THREAD;
i < (thread + 1) * MATRIX_SIZE / NUM_THREAD; i++) {
for (int j = 0; j < MATRIX_SIZE; j++) {
for (int k = 0; k < MATRIX_SIZE; k++) {
matC[i][j] += matA[i][k] * matB[k][j];
}
}
}
return 0;
}
void createMatrix() {
for (int i = 0; i < MATRIX_SIZE; i++) {
for (int j = 0; j < MATRIX_SIZE; j++) {
matA[i][j] = i + j * 2;
matB[i][j] = i * 2 + j;
matC[i][j] = 0.0f;
}
}
}
void printMatrix() {
if (MATRIX_SIZE <= 16) {
printf("Matriz A");
for (int i = 0; i < MATRIX_SIZE; i++) {
printf("\n");
for (int j = 0; j < MATRIX_SIZE; j++) {
printf("%f ", matA[i][j]);
}
}
printf("\n\n");
printf("Matriz B");
for (int i = 0; i < MATRIX_SIZE; i++) {
printf("\n");
for (int j = 0; j < MATRIX_SIZE; j++) {
printf("%f ", matB[i][j]);
}
}
printf("\n\n");
printf("Multiplying matrix A with B");
for (int i = 0; i < MATRIX_SIZE; i++) {
printf("\n");
for (int j = 0; j < MATRIX_SIZE; j++) {
printf("%f ", matC[i][j]);
}
}
}
}
int main() {
pthread_t threads[NUM_THREAD];
clock_t start, end;
createMatrix();
start = clock();
for (int i = 0; i < NUM_THREAD; i++) {
// pthread_create(&threads[i], NULL, multiplicationNormal, NULL);
pthread_create(&threads[i], NULL, multiplicationAVX, NULL);
}
for (int i = 0; i < NUM_THREAD; i++) {
pthread_join(threads[i], NULL);
}
end = clock();
printMatrix();
printf("\n\n使用的线程数 -> %d\n", NUM_THREAD);
printf("\n矩阵大小 -> %d\n", MATRIX_SIZE);
printf("\n程序运行时间(毫秒) -> %f\n\n", (float)(end - start) * 1000 / CLOCKS_PER_SEC);
}

版权声明:本文由 iiYK 原创,允许转载,转载时请注明文章出处 AVX指令集加速矩阵乘法 – iiYK

AVX指令集加速矩阵乘法相关推荐

  1. 【27】SIMD:如何加速矩阵乘法?

    [计算机组成原理]学习笔记--总目录 [27]SIMD:如何加速矩阵乘法? 引言 一.超线程:Intel 多卖给你的那一倍 CPU 1.背景 2.超线程(Hyper-Threading)技术 二.SI ...

  2. AVX指令集实现矩阵乘

    本节矩阵乘选择方阵 思想:c语言默认按行优先存储,矩阵a * b,a的行连续,可以连续访存,大大提高效率:但是b要按列取数,所以去b的列向量浪费时间,解决办法是:将b转置存储,这样b就可以按行进行连续 ...

  3. X86 SSE/AVX指令集加速学习

    在看了刘文志的<并行编程方法与优化实践>后决定写一写书中的例子或者实际工程中用到加速的一些sample,这本书的pdf我也有,可以在下面留言,我发给你. 1. 使用SSE指令实现了一些简单 ...

  4. CUDA实例——加速矩阵乘法

    Ref: CUDA C programing guide https://docs.nvidia.com/cuda/cuda-runtime-api/index.html 一 什么是CUDA? CUD ...

  5. CUDA加速计算矩阵乘法进阶玩法(共享内存)

    CUDA加速计算矩阵乘法&进阶玩法~共享内存 一.基础版矩阵乘法 二.为什么可以利用共享内存加速矩阵乘法 1.CUDA内存读写速度比较 2.申请共享内存 三.改进版矩阵乘法(利用共享内存) 一 ...

  6. CUDA实例系列一: 矩阵乘法优化

    CUDA实例系列一----矩阵乘法优化 很多朋友在学习CUDA的时候都会面临一个题目----矩阵乘法, 这也是CUDA最广泛的应用之一. 本文将详细讲解如何利用GPU加速矩阵乘法的计算. 话不多说, ...

  7. ncnn 框架分析 openmp多核加速 缓存 仿存 cache 快速矩阵乘法 单指令多数据指令SIMD

    ncnn 框架分析 本文github链接 博文末尾支持二维码赞赏哦 _ 在ncnn中建立新层 ncnn 下载编译使用 参考1 参考2 1. param 和 bin 文件分析 param 7767517 ...

  8. c++的矩阵乘法加速trick

    c++的矩阵乘法加速trick 最近读RNNLM的源代码,发现其实现矩阵乘法时使用了一个trick,这里描述一下这个trick. 首先是正常版的矩阵乘法(其实是矩阵乘向量) void matrixXv ...

  9. 基于PYNQ-Z2开发板实现矩阵乘法加速详细流程

    基于PYNQ-Z2开发板实现矩阵乘法加速 主要内容 1.在Vivado HLS中生成矩阵乘法加速的IP核. 2.在Vivado中完成Block Design. 3.在Jupyter Notebook上 ...

最新文章

  1. HDU 2444 The Accomodation of Students
  2. Java学习—— for循环
  3. 【css】报错,错误代码77,CURLE_SSL_CACERT_BADFILE (77)解决方法
  4. docker always_介绍两款Docker可视化工具
  5. stm32串口传输数据第一个数据被吞_stm32串口发送数据复位 第一个数据丢失
  6. 曹大带我学 Go(12)—— 面向火焰图编程
  7. 静态时序分析——基础概念
  8. 【SLAM】安装 g2o_viewer
  9. 前端学习(872):注册事件兼容性处理
  10. (JAVA)IO流之读写单个字节和复制文本文件
  11. find函数常见错误_终于找到你,查找函数,find必不可少
  12. 【elasticsearch】 elasticsearch document 路由 (routing) 到shard
  13. V-rep学习笔记:机器人逆运动学数值解法(Cyclic Coordinate Descent Method)
  14. python 组合优化 回撤最小_【策略回测】多因子搭配组合优化(内附bonus)
  15. delphi 软件在线人数统计_【大学分析】计算机爆满,软件爆冷!这所985大学考研分数截然不同!...
  16. Python进阶(十一)装饰器
  17. P4213 【模板】杜教筛(杜教筛)题解
  18. java命令行参数是什么_Java实验课:命令行参数是什么?
  19. 电视家3.0怎么安装到电视上?常用三种方法介绍
  20. 15.编写LED程序及反汇编工具

热门文章

  1. MSComDlg.CommonDialog参数说明
  2. Numpy中的广播和数组运算
  3. wxpython 关闭_wxPython:关闭wxPython程序
  4. VS2015中配置Eigen
  5. ROS机器人开发实践源代码下载
  6. 非隔离小家电220V降5V芯片4个元件供电方案
  7. 打开被独占的文件方法(二) -- 修改句柄访问权限
  8. DEMUX(解扰解复用)
  9. BG2RHE - 用AtmelStudio7给ATMEGA芯片下载Arduino的bootloader的简便方法
  10. 解决罗技PEBBLE鼠标按键不灵敏的问题