Pages

星期三, 9月 30, 2015

[android] 使用NEON指令來加快程式執行速度

在使用NDK+jni使得原生的C/C++程式碼可以在Android上執行後,通常會遇到執行時間太久的問題,畢竟嵌入式機器的運算資源並不像一般PC或server那樣的豐沛,因此需要做加速的動作。而在ARM上,我們可以運用NEON這個SIMD引擎,來幫助我們將部分的運算,藉由將數個資料放置在特定的暫存器中(Multiple Data, MD),再一併進行相同的運算(Single Instrution, SI)來達到加速的目的。以下記錄自己修改的例子,以供大家參考。


http://stackoverflow.com/questions/19256461/android-ndk-eabi-how-to-make-ndk-know-to-use-hard-fp

1. 修改程式碼

假設我們有一段迴圈是計算兩個陣列(Weight與Output)相對應數值相乘後的總和(向量內積):

float Sum = 0;
for (j=0; j<N; j=j++) {
 Sum += Weight[j] * Output[j];
}

因為都是執行相乘的動作,因此,我們可以利用NEON的暫存器與指令,將四組相乘的計算同時進行。我們將程式碼修改如下:

#include <arm_neon.h>

float Sum = 0;
float32x4_t vec_sum, vec_weight, vec_output;
vec_sum = vdupq_n_f32 (0);
for (j=0; j<N; j=j+4) {
 vec_weight = vld1q_f32 (&Weight[j]);
 vec_output = vld1q_f32 (&Output[j]);
 vec_sum = vmlaq_f32 (vec_sum, vec_weight, vec_output);
}
Sum = vgetq_lane_f32(vec_sum, 0) + vgetq_lane_f32(vec_sum, 1) + vgetq_lane_f32(vec_sum, 2) + vgetq_lane_f32(vec_sum, 3);

其中
  • float32x4_t vec_sum, vec_weight, vec_output代表宣告3個各自包含4個32bits float的變數(每一個總共128bits)
  • vec_sum = vdupq_n_f32 (0)代表將一個包含有4個32bits float的變數vec_sum初始值都設為0
  • vec_weight = vld1q_f32 (&Weight[j])代表將從位址&Weight[j]循序讀取4個32bits float數值到vec_weight
  • vec_sum = vmlaq_f32 (vec_sum, vec_weight, vec_output)代表vec_sum=vec_sum+vec_weight*vec_output
  • Sum = vgetq_lane_f32(vec_sum, 0) + vgetq_lane_f32(vec_sum, 1) + vgetq_lane_f32(vec_sum, 2) + vgetq_lane_f32(vec_sum, 3)則是將vec_sum中四個float相加起來存至Sum中

我們可以發現NEON的函式名稱其實隱含了變數的型態以及運算的總類。舉例來說vdupq_n_f32(0)的v代表vector、dup則為duplicate、q則代表quad、n則代表一scalar以及f32則為32bits的float變數。vmlaq_f32中的mla則為multiply accumulate。相對應的指令搜尋及說明可以參考Appendix 1http://blog.csdn.net/fengbingchun/article/details/38085781

最後,為了要可以使用NEON的指令,在檔案最前面需要include <arm_neon.h>

2. 修改Application.mk

將程式build的target指定為有提供NEON指令集的硬體架構,在這邊我們選用的是armeabi-v7a,而armeabi-v7a有armeabi-v7a與armeabi-v7a-hard兩種,這兩種差異在於我們compile時可以選用的選項會有所不同:armeabi-v7a-hard對應-mfloat-abi=hard,而armeabi-v7a則使用-mfloat-abi=softfp

APP_ABI := armeabi-v7a-hard

或是

APP_ABI := armeabi-v7a

3. 修改Android.mk

在LOCAL_CFLAGS加上相對應的編譯選項:-mvectorize-with-neon-quad -mfpu=neon -mfloat-abi=hard  -funsafe-math-optimizations,再進行編譯即可。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := test
LOCAL_SRC_FILES := test1.c test2.c test3.c

LOCAL_CFLAGS := -O2
LOCAL_CFLAGS += -mvectorize-with-neon-quad -mfpu=neon -mfloat-abi=hard  -funsafe-math-optimizations

APP_OPTIM := release

include $(BUILD_SHARED_LIBRARY)

特別注意的是,如果你要使用-mfloat-abi=hard這個選項,那麼你在Application.mk中的target就要選用armeabi-v7a-hard,否則連結的時候會出現錯誤。

from https://sites.google.com/site/hsienchengliao/android/neon

創用 CC 授權條款
本著作由廖憲正製作,以創用CC 姓名標示-非商業性-相同方式分享 4.0 國際 授權條款釋出。

沒有留言:

張貼留言