使用OpenBlas优化

当时是需要对一个终端耗时任务做矩阵加速,我选了OpenBlas开源方案,事实证明在ARM平台上能用的开源的方案中,OpenBlas做的算相当可以的了。以后有时间还想自己实现一下矩阵乘法的加速。做优化这个过程还是很吸引人的。

编译准备

原理还是一样,首先在本地配置安卓的交叉编译环境,编译出OpenBlas的静态库,用于JNI链接。github上有详细的教程,包括我后来的一个编译错误也是在上面找到了解决方案。详见 How to build OpenBLAS for Android

注意教程上建议在做交叉编译之前先构建一个标准工具链,其实就是交叉编译的环境,第一次我图省事只配置了arm-linux-androideabi-gcc的环境变量,make的时候就会出现找不到头文件的错误。

NDK已经提供了一个脚本做这件事情,在路径/Users/wujian/Library/Android/sdk/ndk-bundle/build/tools下有一个make_standalone_toolchain.py脚本,通过执行命令

1
make_standalone_toolchain.py --arch arm --api 21 --install-dir /dst/path/

之后我们对这个文件夹配置环境变量即可。

编译

不需要Fortran,针对ARMv7

1
make TARGET=ARMV7 HOSTCC=gcc CC=arm-linux-androideabi-gcc NOFORTRAN=1 > make.log

编译时间不长,几分钟吧,编译完会提示安装,执行
1
make PREFIX=/dst/path install

最终安装目录下存在头文件和静态库,这个就是我们最终需要的。

AS中使用JNI链接

这里有个很恶心的问题,目前我没有找到gradle中如何配置链接静态库的方法,只能借助Android.mk文件,但是由于AS默认是执行gradle的编译过程,所以需要在build.gradle中禁用JNI(否则他还是那么一套规程),之后手动配置build的过程。
禁用AS默认的JNI目录:

1
2
3
sourceSets.main {
jni.srcDirs = []
}

配置编译规则
1
2
3
4
5
6
7
8
9
10
11
task ndkBuild(type: org.gradle.api.tasks.Exec, description: "compile JNI by NDK") {
commandLine "/Users/wujian/Library/Android/sdk/ndk-bundle/ndk-build",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}

tasks.withType(JavaCompile) {
compileTask->compileTask.dependsOn ndkBuild
}

同时在JNI目录下加入Android.mk和Application.mk文件,如下,具体含义之后再解释,这里先说明配置过程。Android.mk中详细说明了OpenBlas静态库的连接过程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := blas
LOCAL_SRC_FILES := libopenblas_armv7p-r0.2.20.dev.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := math
LOCAL_SRC_FILES := impl.cpp

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS += -mhard-float -D_NDK_MATH_NO_SOFTFP=1
LOCAL_LDFLAGS += -Wl,--no-warn-mismatch -lm_hard
LOCAL_STATIC_LIBRARIES := blas
endif

LOCAL_CFLAGS += -DUSE_JNI
LOCAL_CFLAGS += -DPFFFT_SIMD_DISABLE
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

1
2
APP_ABI := armeabi-v7a
APP_PLATFORM := android-19

再把OpenBlas的头文件加入JNI目录下,include使用即可,接下来就可以make了。

后续问题

我两次做静态链接时都出现了如下问题,第一次是使用NENO库:

后来在网上查了许久,是编译选项和ABI的问题,这在How to build OpenBLAS for Android最后一部分也说明了。我采取的是第二种方案,因为这里是使用Android.mk来配置编译规则的。