首页 > 其他分享 >2024.4.9 avx加速一维卷积操作(汇总)

2024.4.9 avx加速一维卷积操作(汇总)

时间:2024-04-09 17:13:46浏览次数:29  
标签:__ kernel 2024.4 卷积 int64 mm256 set1 pd avx

第三次作业


alt text

提交内容一:

源代码在-O3编译优化下执行结果:
alt text

AVX指令集优化:

// conv_avx.cpp

bool Convolve1D_Ks5_F64_AVX(double* __restrict__ y, const double* __restrict__ x, const double* __restrict__ kernel, int64_t num_pts) {
    constexpr int64_t kernel_size = 5;
    constexpr int64_t ks2 = kernel_size / 2;

    if (num_pts < kernel_size) {
        return false;
    }

    // 初始化卷积核
    __m256d k0 = _mm256_set1_pd(kernel[0]);
    __m256d k1 = _mm256_set1_pd(kernel[1]);
    __m256d k2 = _mm256_set1_pd(kernel[2]);
    __m256d k3 = _mm256_set1_pd(kernel[3]);
    __m256d k4 = _mm256_set1_pd(kernel[4]);

    // AVX指令集下卷积操作
    for (int64_t i = ks2; i <= num_pts - kernel_size; i += 4) {
        // std::cout << i << '\n';
        __m256d x0 = _mm256_loadu_pd(&x[i + 2]);
        __m256d x1 = _mm256_loadu_pd(&x[i + 1]);
        __m256d x2 = _mm256_loadu_pd(&x[i]);
        __m256d x3 = _mm256_loadu_pd(&x[i - 1]);
        __m256d x4 = _mm256_loadu_pd(&x[i - 2]);

        // 对每个卷积操作进行乘加
        __m256d y_val = _mm256_add_pd(
            _mm256_add_pd(
                _mm256_mul_pd(x0, k0),
                _mm256_mul_pd(x1, k1)),
            _mm256_add_pd(
                _mm256_mul_pd(x2, k2),
                _mm256_add_pd(
                    _mm256_mul_pd(x3, k3),
                    _mm256_mul_pd(x4, k4))));

        // 回存
        _mm256_storeu_pd(&y[i], y_val);
    }

    return true;
}

执行结果:
alt text

在这里因为使用double类型,使用_mm256_load_pd和_mm256_store_pd会出现内存地址不对齐,发生段错误;所以使用_mm256_loadu_pd和_mm256_storeu_pd进行存取。另外在conv程序中可以对每次卷积操作进行fma优化。

AVX2相对AVX引入了向量整数运算的支持,引入了gather指令,允许从非连续内存位置加载数据到一个寄存器中,增加了对向量位操作的支持,且对fma操作进行了加强。

fma优化:

# conv_avx_fma.cpp

bool Convolve1D_Ks5_F64_AVX(double* __restrict__ y, const double* __restrict__ x, const double* __restrict__ kernel, int64_t num_pts) {
    constexpr int64_t kernel_size = 5;
    constexpr int64_t ks2 = kernel_size / 2;

    if (num_pts < kernel_size) {
        return false;
    }

    // 初始化卷积核
    __m256d k0 = _mm256_set1_pd(kernel[0]);
    __m256d k1 = _mm256_set1_pd(kernel[1]);
    __m256d k2 = _mm256_set1_pd(kernel[2]);
    __m256d k3 = _mm256_set1_pd(kernel[3]);
    __m256d k4 = _mm256_set1_pd(kernel[4]);

    // AVX指令集下卷积操作
    for (int64_t i = ks2; i <= num_pts - kernel_size; i += 4) {
        // std::cout << i << '\n';
        __m256d x0 = _mm256_loadu_pd(&x[i + 2]);
        __m256d x1 = _mm256_loadu_pd(&x[i + 1]);
        __m256d x2 = _mm256_loadu_pd(&x[i]);
        __m256d x3 = _mm256_loadu_pd(&x[i - 1]);
        __m256d x4 = _mm256_loadu_pd(&x[i - 2]);

        // 对每个卷积操作进行乘加fma乘加
        __m256d y_val = _mm256_setzero_pd();
        y_val = _mm256_fmadd_pd(x0,k0,y_val);
        y_val = _mm256_fmadd_pd(x1,k1,y_val);
        y_val = _mm256_fmadd_pd(x2,k2,y_val);
        y_val = _mm256_fmadd_pd(x3,k3,y_val);
        y_val = _mm256_fmadd_pd(x4,k4,y_val);

        // 回存
        _mm256_storeu_pd(&y[i], y_val);
    }

    return true;
}

执行结果:
alt text

AVX512优化:

bool Convolve1D_Ks5_F64_AVX512(double* __restrict__ y, const double* __restrict__ x, const double* __restrict__ kernel, int64_t num_pts) {
    constexpr int64_t kernel_size = 5;
    constexpr int64_t ks2 = kernel_size / 2;

    if (num_pts < kernel_size) {
        return false;
    }

    // 初始化卷积核
    __m512d k0 = _mm512_set1_pd(kernel[0]);
    __m512d k1 = _mm512_set1_pd(kernel[1]);
    __m512d k2 = _mm512_set1_pd(kernel[2]);
    __m512d k3 = _mm512_set1_pd(kernel[3]);
    __m512d k4 = _mm512_set1_pd(kernel[4]);

    // AVX指令集下卷积操作
    for (int64_t i = ks2; i <= num_pts - kernel_size; i += 8) { // Note: Change stride to 8 for AVX512
        __m512d x0 = _mm512_loadu_pd(&x[i + 2]);
        __m512d x1 = _mm512_loadu_pd(&x[i + 1]);
        __m512d x2 = _mm512_loadu_pd(&x[i]);
        __m512d x3 = _mm512_loadu_pd(&x[i - 1]);
        __m512d x4 = _mm512_loadu_pd(&x[i - 2]);

        // 对每个卷积操作进行乘加fma乘加
        __m512d y_val = _mm512_setzero_pd();
        y_val = _mm512_fmadd_pd(x0, k0, y_val);
        y_val = _mm512_fmadd_pd(x1, k1, y_val);
        y_val = _mm512_fmadd_pd(x2, k2, y_val);
        y_val = _mm512_fmadd_pd(x3, k3, y_val);
        y_val = _mm512_fmadd_pd(x4, k4, y_val);

        _mm512_storeu_pd(&y[i], y_val);
    }

    return true;
}

执行结果:
alt text

源代码 avx2 avx2_fma avx512
85643us 45320us 44870us 46879us

Summary:
通过对一维卷积进行avx优化,可以观察出一些现象:源代码通过手动循环展开已经得到了不错的优化,但通过avx指令集对源代码进行初次优化加速比可以达到1.89接近两倍的加速比(另外可以通过对卷积操作进行并行乘小幅度提高效率,空间换时间);而通过对卷积操作进行fma优化可以进一步小幅度提升执行效率;但进一步使用avx512时执行速度却反而有了退步,通过查阅一些资料和一些重复猜想是cpu执行avx512指令时导致的主频的降频。

https://zhuanlan.zhihu.com/p/430223278?utm_id=0

想要知道答案,我们必须从CPU自身设计入手。通过查阅Intel的wiki,我们可以大致得到结论,就是Intel CPU依赖复杂的机制基于可用headroom进行dynamic frequency scaling。dynamic frequency scaling,顾名思义,其实就是在运行时,根据不同的负载,CPU会主动升频、降频。
而在CPU进行dynamic frequency scaling中不得不提的一点是,在CPU发展过程中,虽然晶体管的数量仍然能勉强维持摩尔定律[4],并且单核越来越复杂,但是芯片的能耗限制却始终没有解除。所以,当进行某些更加复杂、耗能的计算时(例如AVX-512中的FMA计算),CPU必须要保证自身热能与电能的能耗不超过限制,这也是dynamic frequency scaling的作用,就是通过自身主动升频、降频,能够在保证能耗限制的同时,尽可能对程序进行优化。

alt text
alt text

提交内容二:

alt text

分析:
gather函数是avx2引入的用于从非连续内存位置加载数据到一个寄存器中的操作;gather.cpp中通过index[i]*4到对应字节取值。
__m256d _mm256_i64gather_pd(double const* base_addr, __m256i vindex, const int scale);
permute函数是avx2引入的用于对向量进行排列(Permute)的操作;permute.cpp通过控制位imm8进行对aa的置换操作。

参考资料:
AVX512降频:https://zhuanlan.zhihu.com/p/430223278?utm_id=0
AVX操作:https://blog.csdn.net/qq_17075011/article/details/130555559
AVX2 基本命令:https://blog.csdn.net/weixin_44885334/article/details/129157542

标签:__,kernel,2024.4,卷积,int64,mm256,set1,pd,avx
From: https://www.cnblogs.com/jibinghu/p/18124324

相关文章

  • 2024.4.9 AVX加速卷积part2
    AVX加速卷积part2重新构筑下昨天的想法:问题:源程序在O2下的执行时间:经过AVX改进后的执行时间:下面尝试在AVX2基础上改进:AVX与AVX2的主要区别和改进:向量整数指令:AVX主要集中在浮点数运算上,提供了对256位宽SIMD(单指令多数据)向量的支持。AVX2引入了向量整数运算的支持。这......
  • 毕业设计:基于卷积神经网络的条形码识别系统 深度学习 人工智能 计算机视觉
    目录 前言设计思路一、课题背景与意义二、算法理论原理2.1卷积神经网络2.2目标检测三、检测的实现3.1数据集3.2实验环境3.3模型训练最后 前言    ......
  • 2024.4.8 数据结构课件补题
    [AGC055B]ABCSupremacy令ABC分别为1,2,3,然后令\(s_i=(s_i-i)\textmod3\)且结果大于0。然后可以发现三种组合均为连贯的三个相同数。且可以自由移动。可以选择每遇到三个相同数就删掉,或者不断加入栈,如果栈顶三个数相同全部弹出。再比较剩下的数即可。#include<bits......
  • 卷积神经网络python实现的三种方法
    1、介绍TensorFlow、PyTorch和Keras都是流行的深度学习框架,它们都具有成熟的卷积神经网络(CNN)实现。选择哪种框架取决于您的偏好、项目需求以及团队的技术栈。2、特点TensorFlow:TensorFlow是由Google开发的开源深度学习框架,广泛用于生产环境和研究领域。TensorFlow具有丰富的......
  • 深度学习-卷积神经网络--facenet人脸识别--67
    目录1.概述参考链接:人脸识别网络FaceNetfacenet详解1.概述FaceNet是谷歌于[CVPR2015.02](FaceNet:AUnifiedEmbeddingforFaceRecognitionandClustering)发表,提出了一个对识别(这是谁?)、验证(这是用一个人吗?)、聚类(在这些面孔中找到同一个人)等问题的统一解决框架,即它们......
  • Yolov8-pose关键点检测:特征融合 | CAMixing:卷积-注意融合模块和多尺度提取能力 | 202
     ......
  • 云原生周刊:2024 年 K8s 基准报告 | 2024.4.8
    开源项目推荐ArgoCDImageUpdaterArgoCDImageUpdater是一个自动更新ArgoCD管理的Kubernetes工作负载容器镜像的工具。简而言之,它将跟踪ArgoCD应用程序资源上的注释指定的图像版本,并通过使用ArgoCDAPI设置参数覆盖来更新它们。目前,它仅适用于使用Kustomize......
  • 2024.4.8 pytorch框架初上手
    pytorchPyTorch是一个针对深度学习,并且使用GPU和CPU来优化的tensorlibrary(tensor库)中文文档:https://pytorch.org/resources梯度/导数计算#linear.pyimporttorchimportnumpyasnpx=torch.tensor(3,)w=torch.tensor(4.,requires_grad=True)b=t......
  • 2024.4.7 训练1(rating) Codeforces Global Round 25
    https://codeforces.com/contest/1951题解参考:https://zhuanlan.zhihu.com/p/691034931A题一开始的思路比较绕,wa很多发卡了半小时才过。hansun的思路比较硬直,他在极短的时间内过了Ahansun的题解:https://codeforces.com/contest/1951/submission/255262403我的想法是分奇偶情......
  • 空洞卷积 Dilated Convolution
    空洞卷积DilatedConvolution通常的卷积操作,除了需要指定输入输出通道数,还需要确定卷积核大小kernei_size、步长stride、填充大小padding。Conv1d(384,48,kernel_size=3,stride=1,padding=1)空洞卷积则是在此基础上增加了dilation参数,用于控制卷积核的扩张程度。dil......