Travis Reitter
12/11/2024, 5:12 PM<in Gradle task :android:mergeDebugNativeLibs>
Cause: multik-openblas-jvm-0.2.3 extracted from path ~/.gradle/caches/8.9/transforms/4d3515c442b573850b7bc131f7841f9c/transformed/multik-openblas-jvm-0.2.3/linuxX64/libmultik_jni-linuxX64.so is not an ABI
That file (along with an arm64
equivalent) must be shipped in that transitive dependency but why is this build even loading it? I don't have any explicit Linux targets (though I do have native targets for iOS). Everything built fine before adding these couple of new libraries (multik-core
and multi-default
). More details, including my build.gradle.kts
on this issue. Any ideas? Thanks!ephemient
12/11/2024, 6:15 PMephemient
12/11/2024, 6:17 PMTravis Reitter
12/11/2024, 6:25 PMTravis Reitter
12/11/2024, 6:25 PMTravis Reitter
12/11/2024, 6:28 PMephemient
12/11/2024, 6:31 PMephemient
12/11/2024, 6:31 PMTravis Reitter
12/11/2024, 7:13 PMTravis Reitter
12/13/2024, 2:45 PMmultik-default
with multik-kotlin
(and make a small adjustment to the code to fix the build), I can build on both platforms but performance is noticeably slower on Android (emulator and device) than my previous linear algebra library so I'd really like to get the hardware acceleration that `multik-default`/`-openblas` should provide.Travis Reitter
12/13/2024, 3:00 PMephemient
12/13/2024, 4:31 PMTravis Reitter
12/13/2024, 4:36 PMREADME
- the combination JVM x multik-openblas
x androidArm64
has a ✅Travis Reitter
12/13/2024, 4:36 PMTravis Reitter
12/13/2024, 4:37 PMmultik-openblas
for Android only supports arm64-v8a processors."ephemient
12/13/2024, 4:37 PMephemient
12/13/2024, 4:38 PMTravis Reitter
12/13/2024, 4:39 PMarm64-v8a
ephemient
12/13/2024, 4:39 PMephemient
12/13/2024, 4:40 PMTravis Reitter
12/13/2024, 4:41 PMlinuxX64
for some reason. I did note my config has this:
androidTarget {
publishAllLibraryVariants()
}
I'm not sure if that could be relatedephemient
12/13/2024, 4:41 PMTravis Reitter
12/13/2024, 4:42 PMephemient
12/13/2024, 4:43 PMandroid {
packagingOptions {
exclude("lib/linuxX64/libmultik_jni-linuxX64.so")
exclude("lib/macosArm64/libmultik_jni-macosArm64.dylib")
exclude("lib/macosX64/libmultik_jni-macosX64.dylib")
exclude("lib/mingwX64/libmultik_jni-mingwX64.dll")
}
}
Travis Reitter
12/13/2024, 4:47 PMbuild.gradle.kts
, right? I still get the original error:
:android:mergeDebugNativeLibs
Cause: multik-openblas-jvm-0.2.3 extracted from path ~/.gradle/caches/8.9/transforms/aa9c19370bfe38aa092acebe2995ebfb/transformed/multik-openblas-jvm-0.2.3/linuxX64/libmultik_jni-linuxX64.so is not an ABI
Travis Reitter
12/13/2024, 4:48 PMephemient
12/13/2024, 4:49 PMephemient
12/13/2024, 4:49 PMTravis Reitter
12/13/2024, 4:52 PMTravis Reitter
12/13/2024, 4:59 PMmultik-default
(which pulls in multik-openblas
) for Android seems to be a commonly-intended use case but I haven't found any full working example build.gradle.kts
for thatTravis Reitter
12/13/2024, 5:03 PM~/.gradle/caches/8.9/transforms/aa9c19370bfe38aa092acebe2995ebfb/transformed/multik-openblas-jvm-0.2.3/
contains arm64-v8a/libmultik_jni-androidArm64.so
and linuxX64/libmultik_jni-linuxX64.so
but none of the others listed in the upstream config. Maybe they get filtered out earlier?Travis Reitter
12/13/2024, 5:06 PM~/.gradle/caches/8.9/transforms/aa9c19370bfe38aa092acebe2995ebfb/transformed/multik-openblas-jvm-0.2.3/linuxX64/
I can build and performance looks good 😄
So I just need a fix that will ensure that even if I do a clean buildTravis Reitter
12/13/2024, 5:33 PMlinuxX64
directory
But if I delete all of ~/.gradle
, that file does get restored and the build fails as it did originallyTravis Reitter
12/13/2024, 5:40 PMkotlin {
...
sourceSets {
commonMain.dependencies {
...
implementation(libs.multik.core)
implementation(libs.multik.default)
implementation(libs.kotlinx.serialization.json)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
}
Travis Reitter
12/13/2024, 5:46 PMPavel Gorgulov
12/15/2024, 12:33 PMmultik-openblas
with Android isn’t straightforward. That’s why I recommend using multik-kotlin
instead.
Here’s some details:
• You need to build LAPACK and OpenBLAS specifically for Android. I’ve been building them using GCC and GFortran, but since Google switched to Clang with SDK 20+, I had to manually configure the NDK with the necessary tools: https://github.com/devcrocod/android-gfortran. Unfortunately, I only managed to get it working on older Android versions. To fix this, the build needs to be moved entirely to Clang and Flang, but OpenBLAS has some issues in this setup: https://github.com/Kotlin/multik/issues/111.
• I don’t have a dedicated artifact for Android. Native libraries are bundled together, and Android tries to preload all of them, which causes problems. At the very least, an Android-specific target is required, ideally with additional separation by architectures.
• Some methods require linking with an additional library: https://github.com/Kotlin/multik/issues/127.
So, here are your options:
1. Use multik-kotlin
only. The performance difference on mobile devices doesn’t seem significant
2. If you still want to use OpenBLAS, you need to remove all native libraries except the Android one and ensure it stays in the correct folder.
It seems the solution you received should work. Try building the apk and check what’s inside itTravis Reitter
12/16/2024, 5:55 PM.apk
and these are included:
lib//macosX64
lib//macosX64/libmultik_jni-macosX64.dylib
lib//mingwX64
lib//mingwX64/libmultik_jni-mingwX64.dll
lib//x86
lib//x86/libandroidx.graphics.path.so
lib//arm64-v8a
lib//arm64-v8a/libmultik_jni-androidArm64.so
lib//arm64-v8a/libandroidx.graphics.path.so
lib//macosArm64
lib//macosArm64/libmultik_jni-macosArm64.dylib
lib//x86_64
lib//x86_64/libandroidx.graphics.path.so
Obviously, I don't want to include any platform but arm64-v8a
. Interesting that I'm apparently including libandroidx
for a couple bogus platforms as well. Maybe this is an advantage of App bundles? Do they automatically do that?Travis Reitter
12/16/2024, 5:56 PMExecution failed for task ':android:packageReleaseBundle'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.PackageBundleTask$BundleToolWorkAction
> File 'root/lib/macosArm64/libmultik_jni-macosArm64.dylib' uses reserved file or directory name 'lib'.
and the build failsTravis Reitter
12/16/2024, 5:57 PM:packageReleaseBundle
step, it would work well enough. If you know how to do that, I'd appreciate any tipsTravis Reitter
12/16/2024, 6:00 PMmultik-kotlin
and test on-device to see if performance is adequate. My previous solution was similar in Java (not taking advantage of the hardware) so performance should be comparable. Though, at least in the Android emulator, performance seemed worse. If it's OK on a real device, that's good enough though. And I saw some places in my own code above multik
that I might be able to optimize a little tooTravis Reitter
12/16/2024, 6:18 PMPavel Gorgulov
12/17/2024, 11:45 AMI built the release .apk and these are included:As far as I know, all unnecessary libraries should also be removed, specifically
dylib
and dll
. When I tested it, I encountered errors related to them, but I assume it depends on the Android version.
Honestly, I’m not sure about libandroidx. it didn’t appear in my test project.
uses reserved file or directory name ‘lib’.Yes, exactly. Even though the documentation says that libraries should be split into system-specific folders, Android still tries to preload everything from the lib folder.
Though, at least in the Android emulator, performance seemed worse.Unfortunately, the emulator doesn’t provide a reliable performance answer since there are many nuances. It’s always better to rely on a physical device. However, I measured performance only on desktop chips, and I don’t have much knowledge about applying benchmarks on mobile devices. The context of usage is also important here: how large the arrays are, what kind of arrays they are, and how frequently various operations are applied to them.
maybe if I could manually list in my build config the files to delete before the finalYou can exclude native library via `packagingOptions`:step, it would work well enough. If you know how to do that, I'd appreciate any tips:packageReleaseBundle
android {
packagingOptions {
exclude("lib/**/libmultik_jni-macosX64.dylib")
exclude("lib/**/libmultik_jni-macosArm64.dylib")
exclude("lib/**/libmultik_jni-mingwX64.dll")
exclude("lib/**/libmultik_jni-windowsX86.dll")
exclude("lib/macosX64/**")
exclude("lib/macosArm64/**")
exclude("lib/mingwX64/**")
}
}
or
tasks.register<Delete>("cleanNativeLibs") {
delete(fileTree("build/intermediates") {
include("**/*.dylib", "**/*.dll")
})
delete(fileTree("src/main/jniLibs") {
include("macosX64/**", "macosArm64/**", "mingwX64/**")
})
}
tasks.named("packageReleaseBundle") {
dependsOn("cleanNativeLibs")
}
Actually, I'm not sure about x86 and x86_64, but you can check firstly this approachTravis Reitter
12/18/2024, 5:46 AMbuild.gradle.kts
, right? Should the tasks.*
rules go within android {}
or at the top level? Both failed for me with Task with name 'packageReleaseBundle' not found in project ':android'
. When I check my gradle task list, I see packageReleaseBundle
but it's under [project] > android > Tasks > other
. Should that difference matter?Pavel Gorgulov
12/18/2024, 12:55 PMTravis Reitter
12/18/2024, 5:20 PMTravis Reitter
12/18/2024, 5:23 PMUnfortunately, the emulator doesn’t provide a reliable performance answer since there are many nuances. It’s always better to rely on a physical device.definitely true. I've seen cases (but not all cases) where the Android emulator (on Apple Silicon) can perform better than an actual Android device. My main observation was that performance was noticeably worse compared to my previous linear algebra library but of course 1) I could be misremembering the prior performance as better than it was 2) I may have upgraded Android Studio and dependencies in the meantime in a way that hurt performance overall
Travis Reitter
12/18/2024, 5:27 PMsolve()
on about a hundred 63x3 matricies (along with about a hundred transpose()
, cross products, etc). I could believe hardware acceleration could make a visible impact but I could also believe the Kotlin implementation is good enough for my needs. But I'll also be scaling that up to potentially low-thousands of operations instead of a hundred of eachTravis Reitter
12/18/2024, 5:27 PM