美文网首页
在Android中使用Neon指令优化并行效率

在Android中使用Neon指令优化并行效率

作者: 郑海鹏 | 来源:发表于2020-06-14 22:51 被阅读0次

    Neon指令简介
    普通运算都是单指令单数据的,例如加法同时只能对两个数做加法运算,得到一个结果。 Neon 指令是单指令多数据的指令(Single Instruction Multiple Data,简称SIMD),可以对一组数据同时执行一个指令,从而大幅提高并行运算的效率。

    Neon 指令本身没什么难度,主要是得熟悉 API,我们先看例子,再看API。

    一、使用 Neon 指令将图片转为灰度图

    代码从 Java 层传入一张 1600 x 1245 的图片,独立执行 20 次,耗时 5284 ms,平均每次 264ms 。
    作为对比,使用相同算法,用普通C++实现,耗时 12826ms,平均每次 641ms 。

    使用 Neon 的代码:

    jintArray deColorUseNeon(JNIEnv *env, jsize size, jint* colors) {
        // 保存结果(去色后的图片)
        auto *p = new unsigned char [size * 4];
    
        // 像素的头指针
        const auto* colorsHead = (const unsigned char* )colors;
    
        // 8(每个颜色8-bit) x 8(一次处理8个像素) x 4(ARGB)
        uint8x8x4_t currentPixels;
    
        unsigned char c38[] = {38, 38, 38, 38, 38, 38, 38, 38};
        uint8x8_t _38 = vld1_u8(c38);
        unsigned char c75[] = {75, 75, 75, 75, 75, 75, 75, 75};
        uint8x8_t _75 = vld1_u8(c75);
        unsigned char c15[] = {15, 15, 15, 15, 15, 15, 15, 15};
        uint8x8_t _15 = vld1_u8(c15);
    
        for (unsigned int i = 0; i < size * 4; i += 32) {
            // 读取 8 个像素
            currentPixels = vld4_u8(colorsHead + i);
    
            // 利用公式  (r * 38 + g * 75 + b * 15) / 128 计算灰度值:
    
            // 乘以每个颜色各自的系数
            uint16x8_t tempR = vmull_u8(currentPixels.val[2], _38);
            uint16x8_t tempG = vmull_u8(currentPixels.val[1], _75);
            uint16x8_t tempB = vmull_u8(currentPixels.val[0], _15);
    
            // 相加
            uint16x8_t sum = vaddq_u16(tempR, tempG);
            sum = vaddq_u16(sum, tempB);
    
            // 除以 128,得到灰色通道的值
            uint8x8_t gray = vshrn_n_u16(sum, 7);
    
            currentPixels.val[2] = gray;
            currentPixels.val[1] = gray;
            currentPixels.val[0] = gray;
    
            // 保存到结果中
            vst4_u8(p + i, currentPixels);
        }
    
        jintArray array = env->NewIntArray(size);
        env->SetIntArrayRegion(array, 0, size, (const jint *)p);
        return array;
    }
    

    传统方式实现:

    jintArray deColor(JNIEnv *env, jsize size, jint* colors) {
        // 保存结果(去色后的图片)
        jint *p = new jint[size];
    
    
        unsigned char r, g, b;
        unsigned char temp;
        int* num;
        int alpha;
        for (unsigned int i = 0; i < size; i++) {
            num = colors + i;
            alpha = ((*num) >> 24) & 0xFF;
            r = ((*num) >> 16) & 0xFF;
            g = ((*num) >> 8) & 0xFF;
            b = (*num) & 0xFF;
            // 利用公式  (r * 38 + g * 75 + b * 15) / 128 计算灰度值:
            temp =  (r * 38 + g * 75 + b * 15) >> 7;
            p[i] = (alpha << 24) | (temp << 16) | (temp << 8) | temp;
        }
    
        jintArray array = env->NewIntArray(size);
        env->SetIntArrayRegion(array, 0, size, p);
        return array;
    }
    

    两者的去色效果是相同的。


    二、指令集介绍

    使用 Neon 指令需要在 C++ 中引入头文件 <arm_neon.h>
    这个头文件中定义了支持的数据类型和数据结构,以及各个指令函数。

    (1) 数据结构

    数据结构有两类: (1) 基本的一维向量;(2) 多维向量。

    基本的一维向量:
    数据类型的命名规则: [type][size] x [count] _t
    例如 int8x8_t 表示 int 类型的向量,单个元素占16 bit,有4个元素,总共占据64 bit。
    例如 uint32x4_t 表示 unsigned int 类型的向量,单个元素的大小是32 bit,有4个元素,总共占128 bit。
    常见的类型有:

       int8x8_t,    int16x4_t,   int32x2_t,   int64x1_t, 
       float32x2_t, float16x4_t, uint8x8_t,   uint16x4_t,  
       uint32x2_t,  uint64x1_t
       
       int8x16_t,   int16x8_t,   int32x4_t,   int64x2_t,
       float32x4_t, float16x8_t, uint8x16_t,  uint16x8_t,  
       uint32x4_t,  uint64x2_t
    

    多维向量:
    多维向量是基本数据类型的数组形式,命名规则: [type][size] x [count] x [lengthOfArray] _t。
    例如 uint8x16x4_t 表示 uint8x16_t 的数组,长度为4,常用与ARGB图像处理,它的定义如下:

    typedef struct uint8x16x4_t {
        uint8x16_t val[4];
    } uint8x16x4_t;
    

    (2) 指令

    指令按照作用划分,分为:
    (1) 加载(把数据从内存装载到寄存器);
    (2) 保存(把数据从寄存器保存到内存);
    (3) 加减乘运算(没有除);
    (4) 与(&)、或(|)、异或(^) 运算;
    (5) 其他。

    按照操作的数据大小,分为:
    正常指令、窄指令、长指令、饱和指令等。

    我们按照作用去了解比较好,因为相同作用的指令命名有规律。

    (具体指令比较多,后续每种指令单独附上链接)

    相关文章

      网友评论

          本文标题:在Android中使用Neon指令优化并行效率

          本文链接:https://www.haomeiwen.com/subject/klkqxktx.html