高性能多维数组库 (kotlinx-multik) Experimental
随着 Android 设备算力的提升,移动端对 AI 模型推理、信号处理以及复杂数学运算的需求日益增加。传统的 Kotlin 集合(如 List 或 Array)在处理大规模多维数据时,不仅内存开销巨大,且缺乏矩阵运算的表达力。kotlinx-multik 是 Kotlin 官方推出的高性能多维数组库,它通过灵活的计算引擎切换(JVM、Native、OpenBLAS)和类型安全的维度检查,为 Android 开发者提供了类 NumPy 的开发体验。
技术价值与核心优势
- 多引擎架构:支持纯 Kotlin 实现(Jvm/Native)以保持兼容性,同时也支持接入 OpenBLAS 等高性能 C++/Fortran 库以获得极致性能。
- 维度安全:利用 Kotlin 类型系统(如
D1,D2,DN)在编译期确保矩阵运算的维度匹配,减少运行时崩溃。 - 内存效率:底层采用连续的内存布局,极大减少了对象包装开销,且支持零拷贝的视图(View)操作(如切片、转置)。
- 跨平台支持:原生支持 KMP,逻辑可在 Android 与 iOS 间复用。
依赖配置与版本
kotlin
dependencies {
// 核心 API
implementation("org.jetbrains.kotlinx:multik-core:0.2.3")
// 默认 JVM 引擎 (适用于 Android/JVM)
implementation("org.jetbrains.kotlinx:multik-default:0.2.3")
}groovy
dependencies {
implementation 'org.jetbrains.kotlinx:multik-core:0.2.3'
implementation 'org.jetbrains.kotlinx:multik-default:0.2.3'
}toml
[versions]
multik = "0.2.3"
[libraries]
multik-core = { group = "org.jetbrains.kotlinx", name = "multik-core", version.ref = "multik" }
multik-default = { group = "org.jetbrains.kotlinx", name = "multik-default", version.ref = "multik" }核心抽象:NDArray
multik 的核心模型是 NDArray。它通过类型参数指定元素类型和维度。
API 核心签名
kotlin
public interface NDArray<T, D : Dimension> {
public val data: MemoryView<T> // 扁平化的底层数据视图
public val shape: IntArray // 各维度的长度
public val dim: D // 维度标志(D1, D2 ... DN)
public val size: Int // 总元素个数
// 基础算术运算扩展
public operator fun plus(other: NDArray<T, D>): NDArray<T, D>
public operator fun times(other: NDArray<T, D>): NDArray<T, D>
}多维数组实战
数组创建与切片
multik 提供了极为简洁的 DSL 来初始化数组。
kotlin
import org.jetbrains.kotlinx.multik.api.*
import org.jetbrains.kotlinx.multik.ndarray.data.*
// 创建 2D 矩阵 (3x3)
val matrix = mk.ndarray(mk[mk[1, 2, 3], mk[4, 5, 6], mk[7, 8, 9]])
// 快速创建全零或全一矩阵
val zeros = mk.zeros<Double, D2>(3, 3)
val identity = mk.identity<Double>(3)
// 切片操作 (零拷贝视图)
val subMatrix = matrix[0..1, 1..2] // 获取前两行,中间两列kotlin
// 矩阵乘法 (点积)
val a = mk.ndarray(mk[1, 2, 3], 1, 3)
val b = mk.ndarray(mk[4, 5, 6], 3, 1)
val result = a dot b
// 广播机制 (Broadcasting)
val c = mk.ndarray(mk[10, 20, 30])
val broadcasted = matrix + c // 向量 c 将自动应用到矩阵的每一行底层机制:引擎切换与内存布局
动态引擎发现
multik 采用了插件化的引擎架构。在运行时,可以通过 mk.engine = EngineType.KOTLIN 动态指定使用的计算后端。
- multik-kotlin:纯 Kotlin 实现,无平台限制,但性能一般。
- multik-default:针对 JVM 优化的版本,使用了更高效的循环和向量化。
- multik-openblas:通过 JNI/C-Interop 调用底层线性代数库,适合大规模科学计算。
内存布局优化
multik 并没有使用 Array<Array<T>> 这种嵌套结构,而是将多维数组映射为一维的连续内存块(MemoryView)。
- Stride(步长):通过计算步长来定位多维坐标在扁平化数组中的位置。
- 零拷贝视图:转置(transpose)或切片(slice)操作仅仅是创建了一个拥有不同
shape和stride的新视图,而底层数据完全不发生拷贝。
工程实践准则
Android 端的引擎选择
在 Android 上,建议优先使用 multik-default。如果涉及极大规模的矩阵运算,可以考虑集成 multik-openblas,但需注意这会显著增加 APK 体积(需包含各架构的 .so 文件)。
避免频繁创建临时对象
虽然 multik 做了大量优化,但频繁的矩阵运算仍会产生中间 NDArray 对象。在高性能循环中,尽量使用原地更新(In-place)的操作(如果 API 支持)。
R8/Proguard 配置
由于 multik 涉及引擎的动态加载,混淆时需保留相关类:
proguard
-keep class org.jetbrains.kotlinx.multik.api.** { *; }
-keep class org.jetbrains.kotlinx.multik.ndarray.** { *; }