在使用NDK+jni使得原生的C/C++程式碼可以在Android上執行後,通常會遇到執行時間太久的問題,畢竟嵌入式機器的運算資源並不像一般PC或server那樣的豐沛,因此需要做加速的動作。而在ARM上,我們可以運用NEON這個SIMD引擎,來幫助我們將部分的運算,藉由將數個資料放置在特定的暫存器中(Multiple Data, MD),再一併進行相同的運算(Single Instrution, SI)來達到加速的目的。以下記錄自己修改的例子,以供大家參考。
- 開發環境:Android Studio 1.3.1
- NDK版本:android-ndk-r10e
- 參考網頁:
- 指令
- NEON
- 範例
- -mfloat-abi=hard or softfp
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 1或 http://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
沒有留言:
張貼留言