2024年国科大-高性能计算编程-作业三

作业题目

提交内容一:改写代码

原代码编译运行结果

使用avx进行向量化

  • 编译运行结果

  • 代码详情
bool Convolve1D_Ks5_F64_cpp_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;
    }
    for (int64_t i = ks2; i < num_pts - ks2; i += 4) {
        __m256d k2 = _mm256_set1_pd(kernel[ks2 - 2]);
        __m256d k1 = _mm256_set1_pd(kernel[ks2 - 1]);
        __m256d k0 = _mm256_set1_pd(kernel[ks2]);
        __m256d kn1 = _mm256_set1_pd(kernel[ks2 + 1]);
        __m256d kn2 = _mm256_set1_pd(kernel[ks2 + 2]);

        __m256d x2 = _mm256_loadu_pd(x + i + 2);
        __m256d x1 = _mm256_loadu_pd(x + i + 1);
        __m256d x0 = _mm256_loadu_pd(x + i);
        __m256d xn1 = _mm256_loadu_pd(x + i - 1);
        __m256d xn2 = _mm256_loadu_pd(x + i - 2);

        __m256d y0 = _mm256_mul_pd(k2, x2);
        __m256d y1 = _mm256_mul_pd(k1, x1);
        __m256d y2 = _mm256_mul_pd(k0, x0);
        __m256d y3 = _mm256_mul_pd(kn1, xn1);
        __m256d y4 = _mm256_mul_pd(kn2, xn2);
        
        __m256d result = _mm256_add_pd(_mm256_add_pd(_mm256_add_pd(y0, y1), _mm256_add_pd(y2, y3)), y4);
        
        _mm256_storeu_pd(y + i, result);
    }
    return true;
}

使用avx2进行向量化

  • avx2在avx的基础上进行了一些扩展,新增了FMA(融合乘法和加法)、gather(从非连续内存位置加载数据到一个寄存器中)、permute(在寄存器内重新排列数据)等新的方法
  • 因此我在代码中尝试融入fma方法,试试能不能再次降低运行时间
  • 编译运行时间

  • 代码详情
bool Convolve1D_Ks5_F64_cpp_AVX2(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;
    }

    for (int64_t i = ks2; i < num_pts - ks2; i += 4) {
        __m256d k2 = _mm256_set1_pd(kernel[ks2 - 2]);
        __m256d k1 = _mm256_set1_pd(kernel[ks2 - 1]);
        __m256d k0 = _mm256_set1_pd(kernel[ks2]);
        __m256d kn1 = _mm256_set1_pd(kernel[ks2 + 1]);
        __m256d kn2 = _mm256_set1_pd(kernel[ks2 + 2]);

        __m256d x2 = _mm256_loadu_pd(x + i + 2);
        __m256d x1 = _mm256_loadu_pd(x + i + 1);
        __m256d x0 = _mm256_loadu_pd(x + i);
        __m256d xn1 = _mm256_loadu_pd(x + i - 1);
        __m256d xn2 = _mm256_loadu_pd(x + i - 2);

        __m256d result = _mm256_setzero_pd();
        result = _mm256_fmadd_pd(k2, x2, result);
        result = _mm256_fmadd_pd(k1, x1, result);
        result = _mm256_fmadd_pd(k0, x0, result);
        result = _mm256_fmadd_pd(kn1, xn1, result);
        result = _mm256_fmadd_pd(kn2, xn2, result);

        _mm256_storeu_pd(y + i, result);
    }

    return true;
}

运行时间对比

原代码AVXAVX2
timing13241us10648us11179us
  • 在同样使用-O2优化级别的前提下,使用avx向量化内嵌指令修改后的代码运行时间提升了20%左右
  • 使用avx2中的fma特性融合加法和乘法后,运行时间反而略微上涨

提交内容二:编译运行代码

  • gather.cpp

  • permute.cpp

总结分析

  • 使用向量化内嵌指令可以大幅度提升代码的运行速度
  • 使用avx2中的fma融合加法和乘法操作不一定能提升代码运行速度,反而可能产生不好的影响。在本次作业的代码里,原来的各个乘法之间没有依赖关系,__m256d y0__m256d y4这五个乘法运算可能并行地运行。但是如果使用fma将乘法操作和最后的加法操作进行融合,代码就需要进行一次乘法并累加到result后才能进行下一次乘法,这样反而浪费了时间

自评分及理由

自评分

  • 10分

评分理由

  • 完成提交内容一:使用avx和avx2的fma特性对原代码进行优化、比较代码运行时间,并对产生的现象进行了分析(7分)
  • 完成提交内容二:编译运行gather.cpppermute.cpp(3分)
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值