From 91fc08c64114d75880e9fde42d85836d93d0c02a Mon Sep 17 00:00:00 2001 From: Kevin Brothaler Date: Tue, 20 Dec 2022 15:15:59 -0500 Subject: [PATCH] Build a vfpv4 library for armeabi-v7a and do runtime detection to select the right library --- .../com/whispercppdemo/whisper/LibWhisper.kt | 41 ++++++++++++++++++- .../app/src/main/jni/whisper/Android.mk | 29 +++++-------- .../app/src/main/jni/whisper/Whisper.mk | 18 ++++++++ 3 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 examples/whisper.android/app/src/main/jni/whisper/Whisper.mk diff --git a/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt b/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt index 69acec1..a6dfdcc 100644 --- a/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt +++ b/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt @@ -1,8 +1,13 @@ package com.whispercppdemo.whisper +import android.os.Build +import android.util.Log import kotlinx.coroutines.* +import java.io.File import java.util.concurrent.Executors +private const val LOG_TAG = "LibWhisper" + class WhisperContext private constructor(private var ptr: Long) { // Meet Whisper C++ constraint: Don't access from more than one thread at a time. private val scope: CoroutineScope = CoroutineScope( @@ -47,7 +52,27 @@ class WhisperContext private constructor(private var ptr: Long) { private class WhisperLib { companion object { init { - System.loadLibrary("whisper") + Log.d(LOG_TAG, "Primary ABI: ${Build.SUPPORTED_ABIS[0]}") + var loadVfpv4 = false + if (isArmEabiV7a()) { + // armeabi-v7a needs runtime detection support + val cpuInfo = cpuInfo() + cpuInfo?.let { + Log.d(LOG_TAG, "CPU info: $cpuInfo") + if (cpuInfo.contains("vfpv4")) { + Log.d(LOG_TAG, "CPU supports vfpv4") + loadVfpv4 = true + } + } + } + + if (loadVfpv4) { + Log.d(LOG_TAG, "Loading libwhisper_vfpv4.so") + System.loadLibrary("whisper_vfpv4") + } else { + Log.d(LOG_TAG, "Loading libwhisper.so") + System.loadLibrary("whisper") + } } // JNI methods @@ -59,3 +84,17 @@ private class WhisperLib { } } +private fun isArmEabiV7a(): Boolean { + return Build.SUPPORTED_ABIS[0].equals("armeabi-v7a") +} + +private fun cpuInfo(): String? { + return try { + File("/proc/cpuinfo").inputStream().bufferedReader().use { + it.readText() + } + } catch (e: Exception) { + Log.w(LOG_TAG, "Couldn't read /proc/cpuinfo", e) + null + } +} \ No newline at end of file diff --git a/examples/whisper.android/app/src/main/jni/whisper/Android.mk b/examples/whisper.android/app/src/main/jni/whisper/Android.mk index 549cdcb..3e3da8b 100644 --- a/examples/whisper.android/app/src/main/jni/whisper/Android.mk +++ b/examples/whisper.android/app/src/main/jni/whisper/Android.mk @@ -1,22 +1,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -WHISPER_LIB_DIR := $(LOCAL_PATH)/../../../../../../../ -LOCAL_LDLIBS := -llog LOCAL_MODULE := libwhisper +include $(LOCAL_PATH)/Whisper.mk +include $(BUILD_SHARED_LIBRARY) -# Make the final output library smaller by only keeping the symbols referenced from the app. -ifneq ($(APP_OPTIM),debug) - LOCAL_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden - LOCAL_CFLAGS += -ffunction-sections -fdata-sections - LOCAL_LDFLAGS += -Wl,--gc-sections - LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL - LOCAL_LDFLAGS += -flto -endif - -LOCAL_CFLAGS += -DSTDC_HEADERS -std=c11 -I $(WHISPER_LIB_DIR) -LOCAL_CPPFLAGS += -std=c++11 -LOCAL_SRC_FILES := $(WHISPER_LIB_DIR)/ggml.c \ - $(WHISPER_LIB_DIR)/whisper.cpp \ - $(LOCAL_PATH)/jni.c - -include $(BUILD_SHARED_LIBRARY) \ No newline at end of file +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + include $(CLEAR_VARS) + LOCAL_MODULE := libwhisper_vfpv4 + include $(LOCAL_PATH)/Whisper.mk + # Allow building NEON FMA code. + # https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures/cpu-features.h + LOCAL_CFLAGS += -mfpu=neon-vfpv4 + include $(BUILD_SHARED_LIBRARY) +endif \ No newline at end of file diff --git a/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk b/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk new file mode 100644 index 0000000..b4f050e --- /dev/null +++ b/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk @@ -0,0 +1,18 @@ +WHISPER_LIB_DIR := $(LOCAL_PATH)/../../../../../../../ +LOCAL_LDLIBS := -llog + +# Make the final output library smaller by only keeping the symbols referenced from the app. +ifneq ($(APP_OPTIM),debug) + LOCAL_CFLAGS += -O3 + LOCAL_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden + LOCAL_CFLAGS += -ffunction-sections -fdata-sections + LOCAL_LDFLAGS += -Wl,--gc-sections + LOCAL_LDFLAGS += -Wl,--exclude-libs,ALL + LOCAL_LDFLAGS += -flto +endif + +LOCAL_CFLAGS += -DSTDC_HEADERS -std=c11 -I $(WHISPER_LIB_DIR) +LOCAL_CPPFLAGS += -std=c++11 +LOCAL_SRC_FILES := $(WHISPER_LIB_DIR)/ggml.c \ + $(WHISPER_LIB_DIR)/whisper.cpp \ + $(LOCAL_PATH)/jni.c \ No newline at end of file