高性能计算-向量化优化和循环展开效率对比(14)

1. 目标

对数组求和,对比ARM(neon)向量化优化(SIMD)和循环展开,还有 O0 O1优化的效率对比。

2. 测试代码

#include <arm_neon.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#define N 1024000


int32_t neonSum(int32_t *arr,int n)
{
    //保存计算元素向量
    int32x4_t vec={0};
    //声明保存中间计算结果向量
    int32x4_t tempSum={0};
    //声明标量保存最终计算结果变量
    int32_t sum=0;
    for(int i=0;i<n/4*4;i+=4)
    {
        //加载4个元素到向量寄存器
        vec = vld1q_s32(&arr[i]);
        //累加每轮元素之和
		//或者使用接口 int32_t vaddvq_s32(int32x4_t a);
        tempSum = vaddq_s32(tempSum,vec);
    }
    //处理结尾数据
    for(int i=n/4*4;i<n;i++)
        sum += arr[i];
    //分别计算 01 23 元素的和,能保存2组求和结果 sum0 sum1 sum0 sum1
    tempSum = vpaddq_s32(tempSum,tempSum);
    //计算 01 元素的和
    tempSum = vpaddq_s32(tempSum,tempSum);
    //获取计算结果
    sum += vgetq_lane_s32(tempSum,0);
    return sum;
} 

void main()
{
    clock_t start,end;
    int32_t arr[N] = {0};
    int32_t sum=0;

    //初始化
    for(int i=0;i<N;i++)
        arr[i] = (int32_t)(rand()%11);

    start = clock();
    #if 1   //neon向量化
    sum = neonSum(arr,N);
    #elif 0 //源码
    for(int i=0;i<N;i++)
        sum+=arr[i];
    #else   //循环展开
    for(int i=0;i<N;i+=4)
    {
        sum+=arr[i];
        sum+=arr[i+1];
        sum+=arr[i+2];
        sum+=arr[i+3];
    }
    #endif
    end = clock();
    
    // for(int i=0;i<N;i++)
    //     printf("%d\n",arr[i]);
    printf("sum %d useSec %f\n",sum,(double)(end-start)/CLOCKS_PER_SEC);
}

3. 编译命令

-fopt-info-all :显示编译优化信息;

O1 O2 默认不开启向量化优化,需要参数指定;O3默认开启向量化优化。

gcc sum_simd.c -o sum_simd_0 -O0 -fopt-info-all
gcc sum_simd.c -o sum_simd_1 -O1 -ftree-vectorize -fopt-info-all
gcc sum_simd.c -o sum_simd_2 -O2 -ftree-vectorize -fopt-info-all
gcc sum_simd.c -o sum_simd_3 -O3 -fopt-info-all

测试脚本

#!/bin/bash
# 编译
dir=out
# 清空文件夹
> "$dir"

echo "start $(date)" >> out

# 要测试的编译优化等级
opt=(0 1 2 3)
# 要测试的函数 0:源码 1:循环展开 2:NEON Intrinsic 函数
func=(0 1 2)   

for lev in "${opt[@]}"; do
    for f in "${func[@]}"; do
        for((i=0;i<4;i++)); do
            yhrun -N1 -n1 -pthcp1 ./sum_simd_"$lev" "$f" | tee -a "$dir"
            # echo "yhrun -N1 -n1 -pthcp1 ./sum_simd_$lev "$f"  | tee -a "$dir"" >> "$dir"
        done
    done
done

echo "end $(date)" >> out

4. 测试数据

优化函数\优化参数 O0 O1 向量化 O2 向量化 O3
源码 0.004388 0.001618 0.001641 0.001591
循环展开 0.003876 0.001675 0.001610 0.001642
NEON Intrinsic SIMD 0.002840 0.001667 0.001596 0.001593

5. 结果分析

(1) O0 优化:neon > 循环展开 > 源码。只有NEON Intrinsic函数在开启了向量化,所以效率最高;循环展开减少了for循环次数效率次之。neon比源码效率提升了 54% ;

(2) O1/O2/O3 编译器向量化优时,源码、循环展开、neon效率差不多;

(3) 向量化优化可以通过 NEON Intrinsic 函数实现,或者使用编译优化选项;但是复杂的循环结构,需要自己手动实现向量化优化代码。

备注:当前数据规模为1024000,预计数据规模的变化几乎没有影响。

posted @ 2024-11-25 22:28  安洛8  阅读(18)  评论(0编辑  收藏  举报