From 19f8048139e0a05f4a511f1781d9b78b82f11260 Mon Sep 17 00:00:00 2001 From: Neuman Vong Date: Sat, 10 Feb 2024 02:39:05 +1100 Subject: [PATCH] whisper.android : how to build with CLBlast (#1809) * FetchContent * OpenCL * Documentation and make optional * Specify GGML build options in build.gradle * Use gradle properties * @ggerganov Co-authored-by: Georgi Gerganov * @gpokat --------- Co-authored-by: Georgi Gerganov --- .github/workflows/build.yml | 16 ++++++- examples/whisper.android/README.md | 44 +++++++++++++++++++ examples/whisper.android/lib/build.gradle | 22 ++++++++++ .../lib/src/main/jni/whisper/CMakeLists.txt | 40 +++++++++++++---- 4 files changed, 113 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae6c4ce9..41cb691c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -416,6 +416,14 @@ jobs: steps: - name: Clone uses: actions/checkout@v3 + with: + path: whisper + + - name: Clone + uses: actions/checkout@v3 + with: + repository: ggerganov/ggml + path: ggml - name: Install Java uses: actions/setup-java@v3 @@ -428,9 +436,15 @@ jobs: - name: Build run: | - cd examples/whisper.android + cd whisper/examples/whisper.android ./gradlew assembleRelease --no-daemon + - name: Build with external ggml + run: | + export PATH_TO_GGML=$PWD/ggml + cd whisper/examples/whisper.android + ./gradlew assembleRelease --no-daemon -PGGML_HOME=$PATH_TO_GGML + android_java: runs-on: ubuntu-latest diff --git a/examples/whisper.android/README.md b/examples/whisper.android/README.md index cf8a328c..a661b323 100644 --- a/examples/whisper.android/README.md +++ b/examples/whisper.android/README.md @@ -12,3 +12,47 @@ To use: (PS: Do not move this android project folder individually to other folders, because this android project folder depends on the files of the whole project.) image + +## CLBlast + +> [!NOTE] +> - OpenCL does not have the same level of support as CUDA or Metal. +> - Turning on CLBlast may degrade OpenCL performance if your device isn't already tuned. See [tuning.md](https://github.com/CNugteren/CLBlast/blob/162783a414969464ce3aa5adf5c2554afa5ee93e/doc/tuning.md#already-tuned-for-devices) for a list of devices that are already tuned and what to do if yours is missing. + +Build CLBlast. + +``` +# In path/to/CLBlast (we assume OpenCL-Headers relative location) +$ANDROID_SDK_PATH/cmake/3.22.1/bin/cmake .. \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_SYSTEM_VERSION=33 \ + -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ + -DCMAKE_ANDROID_NDK=$ANDROID_NDK_PATH \ + -DCMAKE_ANDROID_STL_TYPE=c++_static \ + -DOPENCL_ROOT=$(readlink -f ../../OpenCL-Headers) \ + -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH \ + -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH + +# Build libclblast.so +make -j4 +``` + +Pull `libGLES_mali.so` to `libOpenCL.so`. + +```bash +# In path/to/whisper.android +mkdir lib/src/main/jniLibs/arm64-v8a +adb pull /system/vendor/lib64/egl/libGLES_mali.so lib/src/main/jniLibs/arm64-v8a/libOpenCL.so +``` + +In gradle.properties, set `GGML_HOME` to the location of GGML, as well as +required options for turning on CLBlast. + +``` +GGML_HOME=/path/to/ggml +GGML_CLBLAST=ON +CLBLAST_HOME=/path/to/CLBlast +OPENCL_LIB=/path/to/libOpenCL.so +OPENCL_ROOT=/path/to/OpenCL-Headers +``` + diff --git a/examples/whisper.android/lib/build.gradle b/examples/whisper.android/lib/build.gradle index c32a6899..e4779e26 100644 --- a/examples/whisper.android/lib/build.gradle +++ b/examples/whisper.android/lib/build.gradle @@ -16,6 +16,28 @@ android { ndk { abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' } + externalNativeBuild { + cmake { + // When set, builds whisper.android against the version located + // at GGML_HOME instead of the copy bundled with whisper.cpp. + if ( + project.hasProperty('GGML_HOME') && + project.findProperty('GGML_CLBLAST') == 'ON' + ) { + // Turning on CLBlast requires GGML_HOME + arguments "-DGGML_HOME=${project.property('GGML_HOME')}", + "-DGGML_CLBLAST=ON", + "-DOPENCL_LIB=${project.property('OPENCL_LIB')}", + "-DCLBLAST_HOME=${project.property('CLBLAST_HOME')}", + "-DOPENCL_ROOT=${project.property('OPENCL_ROOT')}", + "-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH", + "-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH" + } else if (project.hasProperty('GGML_HOME')) { + arguments "-DGGML_HOME=${project.property('GGML_HOME')}" + } + + } + } } buildTypes { diff --git a/examples/whisper.android/lib/src/main/jni/whisper/CMakeLists.txt b/examples/whisper.android/lib/src/main/jni/whisper/CMakeLists.txt index 390fd196..49f34479 100644 --- a/examples/whisper.android/lib/src/main/jni/whisper/CMakeLists.txt +++ b/examples/whisper.android/lib/src/main/jni/whisper/CMakeLists.txt @@ -3,17 +3,28 @@ cmake_minimum_required(VERSION 3.10) project(whisper.cpp) set(CMAKE_CXX_STANDARD 11) -set(WHISPER_LIB_DIR ${CMAKE_SOURCE_DIR}/../../../../../../../) +set(WHISPER_LIB_DIR ${CMAKE_SOURCE_DIR}/../../../../../../..) + +# Path to external GGML, otherwise uses the copy in whisper.cpp. +option(GGML_HOME "whisper: Path to external GGML source" OFF) set( SOURCE_FILES + ${WHISPER_LIB_DIR}/whisper.cpp + ${CMAKE_SOURCE_DIR}/jni.c +) + +if (NOT GGML_HOME) + set( + SOURCE_FILES + ${SOURCE_FILES} ${WHISPER_LIB_DIR}/ggml.c ${WHISPER_LIB_DIR}/ggml-alloc.c ${WHISPER_LIB_DIR}/ggml-backend.c ${WHISPER_LIB_DIR}/ggml-quants.c - ${WHISPER_LIB_DIR}/whisper.cpp - ${CMAKE_SOURCE_DIR}/jni.c -) + + ) +endif() find_library(LOG_LIB log) @@ -24,12 +35,12 @@ function(build_library target_name) ${SOURCE_FILES} ) - target_link_libraries(${target_name} ${LOG_LIB} android) - if (${target_name} STREQUAL "whisper_v8fp16_va") target_compile_options(${target_name} PRIVATE -march=armv8.2-a+fp16) + set(GGML_COMPILE_OPTIONS -march=armv8.2-a+fp16) elseif (${target_name} STREQUAL "whisper_vfpv4") target_compile_options(${target_name} PRIVATE -mfpu=neon-vfpv4) + set(GGML_COMPILE_OPTIONS -mfpu=neon-vfpv4) endif () if (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") @@ -43,9 +54,20 @@ function(build_library target_name) target_link_options(${target_name} PRIVATE -flto) endif () -endfunction() -build_library("whisper") # Default target + if (GGML_HOME) + include(FetchContent) + FetchContent_Declare(ggml SOURCE_DIR ${GGML_HOME}) + FetchContent_MakeAvailable(ggml) + + target_compile_options(ggml PRIVATE ${GGML_COMPILE_OPTIONS}) + target_link_libraries(${target_name} ${LOG_LIB} android ggml) + else() + target_link_libraries(${target_name} ${LOG_LIB} android) + endif() + + +endfunction() if (${ANDROID_ABI} STREQUAL "arm64-v8a") build_library("whisper_v8fp16_va") @@ -53,4 +75,6 @@ elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a") build_library("whisper_vfpv4") endif () +build_library("whisper") # Default target + include_directories(${WHISPER_LIB_DIR})