Rust高性能线性代数库openblas-src的使用,openblas-src为Rust提供BLAS和LAPACK的绑定与加速功能
openblas-src 包通过 OpenBLAS 提供了 BLAS 和 LAPACK 的源。
配置
支持以下 Cargo 功能:
cache
在共享目录中构建,而不是在target
中(见下文),cblas
构建 CBLAS(默认启用),lapacke
构建 LAPACKE(默认启用),static
静态链接到 OpenBLAS,system
跳过构建捆绑的 OpenBLAS。
注意:在 Windows 上,无法从源代码构建 OpenBLAS。应使用 system
功能。
依赖项
如果您想从源代码构建 OpenBLAS,需要安装以下依赖项:
- HOSTCC 编译器(例如
gcc
、clang
或icc
), make
,- 目标架构的 CC 编译器(例如,对于
aarch64
使用aarch64-linux-gnu-gcc
), - 目标架构的 Fortran 编译器(例如
gfortran
、flang
或ifort
), 如果未检测到 Fortran 编译器,应将标志NOFORTRAN
设置为1
,并且OpenBLAS
将仅编译 BLAS 和 f2c 转换的 LAPACK。更多信息,请参考 当没有 Fortran 编译器可用时使用 f2c 转换的 LAPACK。
缓存
cache
功能允许在具有不同 target
目录的 crate 之间重用 OpenBLAS 构建产物。这避免了不必要地重新构建 OpenBLAS。然而,这也阻止了 cargo clean
正常工作,因为上述构建产物不会被该命令删除。
OpenBLAS 二进制文件将放置在 ${XDG_DATA_HOME}/openblas_build/[构建配置对象的哈希值]
。例如,构建带 LAPACK 和不带 LAPACK 的构建将放置在不同的目录中。如果您将 OpenBLAS 构建为共享库,则需要将上述目录添加到 LD_LIBRARY_PATH
(对于 Linux)或 DYLD_LIBRARY_PATH
(对于 macOS)。由于在 Windows 上不支持从源代码构建(见下一节),此功能也不支持。
Windows 和 vcpkg
在 Windows 上,openblas-src
依赖 vcpkg 来查找 OpenBLAS。在构建之前,您必须为目标三元组和链接类型安装正确的 OpenBLAS。例如,要为 x86_64-pc-windows-msvc
工具链动态链接,为 x64-windows
三元组安装 openblas
:
vcpkg install openblas --triplet x64-windows
要静态链接 OpenBLAS,为 x64-windows-static-md
三元组安装 openblas
:
vcpkg install openblas --triplet x64-windows-static-md
要静态链接 OpenBLAS 和 C 运行时(CRT),为 x64-windows-static
三元组安装 openblas
:
vcpkg install openblas --triplet x64-windows-static
并使用 +crt-static
选项构建
RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows-msvc
详情请参见 Rust 参考中的“静态和动态 C 运行时”。
环境变量
代理
openblas-src
crate 将检测并使用环境变量中的代理设置,例如 http_proxy
和 https_proxy
,以下载必要的依赖项。
通过 OpenBLAS 的构建系统
根据 OpenBLAS 构建系统,OpenBLAS 使用的变量可以通过环境传递,例如 DYNAMIC_LIST
、NUM_THREADS
。
但是,对于某些变量,openblas-src
crate 将它们重命名为其他名称,以避免与现有环境变量冲突。以下是重命名的变量列表:
OpenBLAS 变量 | openblas-src 变量 |
---|---|
TARGET | OPENBLAS_TARGET |
CC | OPENBLAS_CC |
FC | OPENBLAS_FC |
HOSTCC | OPENBLAS_HOSTCC |
RANLIB | OPENBLAS_RANLIB |
交叉编译
除了向 cargo build
提供 --target
选项外,还必须指定 OpenBLAS 的交叉编译变量。它们可以使用 OPENBLAS_
前缀设置为 cargo build
的环境变量,如下所示:OPENBLAS_CC
、OPENBLAS_FC
、OPENBLAS_HOSTCC
和 OPENBLAS_TARGET
。
如果您不设置这些变量,openblas-build
将尝试检测它们。
对于 OPENBLAS_TARGET
,将使用与 --target
的架构对应的基本目标。
Rust 目标 | OpenBLAS 目标 |
---|---|
aarch64 | ARMV8 |
arm | ARMV6 |
armv5te | ARMV5 |
armv6 | ARMV6 |
armv7 | ARMV7 |
loongarch64 | LOONGSONGENERIC |
mips64 | MIPS64_GENERIC |
mips64el | MIPS64_GENERIC |
riscv64 | RISCV64_GENERIC |
csky | CK860FV |
sparc | SPARCV7 |
对于 OPENBLAS_CC
和 OPENBLAS_HOSTCC
,将使用 cc
crate 来检测编译器。更多信息请参考 cc 文档。
对于 OPENBLAS_FC
,openblas-build
将尝试通过上面设置的 OPENBLAS_CC
检测编译器。它将把 gcc
替换为 gfortran
,clang
替换为 flang
,icc
替换为 ifort
,然后测试 Fortran 编译器是否存在。
注意:如果未检测到 Fortran 编译器,构建标志 NOFORTRAN
将被设置为 1
,并且 OpenBLAS
将仅编译 BLAS 和 f2c 转换的 LAPACK。更多信息,请参考 当没有 Fortran 编译器可用时使用 f2c 转换的 LAPACK。
贡献
非常欢迎您的贡献。请随时提出问题或拉取请求。请注意,为包含在项目中提交的任何贡献将根据 LICENSE.md 中给出的条款进行许可。
以下是一个使用 openblas-src 的完整示例代码:
// 在 Cargo.toml 中添加依赖:
// openblas-src = "0.10.12"
// 示例:使用 openblas-src 进行矩阵乘法
fn main() {
// 注意:openblas-src 主要提供底层绑定,通常通过其他高级库(如 ndarray)使用
// 这里展示一个简单的使用示例
println!("OpenBLAS 已成功链接!");
println!("此 crate 提供了 BLAS 和 LAPACK 的绑定,通常通过其他线性代数库使用");
}
// 更完整的示例,展示如何通过 ndarray 使用 BLAS 功能
/*
// Cargo.toml 依赖:
// openblas-src = "0.10.12"
// ndarray = { version = "0.15", features = ["blas"] }
// blas-src = { version = "0.9", features = ["openblas"] }
use ndarray::prelude::*;
use ndarray::linalg::general_mat_vec_mul;
fn main() {
// 创建矩阵和向量
let a = array![[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]];
let x = array![1.0, 2.0, 3.0];
let mut y = Array1::zeros(2);
// 使用 BLAS 进行矩阵向量乘法: y = α * A * x + β * y
let alpha = 1.0;
let beta = 0.0;
general_mat_vec_mul(alpha, &a, &x, beta, &mut y);
println!("Matrix A:\n{:?}", a);
println!("Vector x: {:?}", x);
println!("Result y = A * x: {:?}", y);
}
*/
完整示例代码:
// Cargo.toml 依赖:
// openblas-src = "0.10.12"
// ndarray = { version = "0.15", features = ["blas"] }
// blas-src = { version = "0.9", features = ["openblas"] }
use ndarray::prelude::*;
use ndarray::linalg::general_mat_vec_mul;
fn main() {
// 创建矩阵和向量
let a = array![[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]];
let x = array![1.0, 2.0, 3.0];
let mut y = Array1::zeros(2);
// 使用 BLAS 进行矩阵向量乘法: y = α * A * x + β * y
let alpha = 1.0;
let beta = 0.0;
general_mat_vec_mul(alpha, &a, &x, beta, &mut y);
println!("Matrix A:\n{:?}", a);
println!("Vector x: {:?}", x);
println!("Result y = A * x: {:?}", y);
}
openblas-src:Rust高性能线性代数库
简介
openblas-src是Rust语言中提供BLAS(Basic Linear Algebra Subprograms)和LAPACK(Linear Algebra Package)绑定的高性能线性代数库。它通过OpenBLAS后端提供优化的数值计算功能,特别适合需要进行大规模矩阵运算、线性代数计算的科学计算和机器学习应用。
主要特性
- 完整的BLAS级别1、2、3功能实现
- LAPACK线性代数例程支持
- 多线程并行计算优化
- 跨平台支持(Linux、macOS、Windows)
- 自动检测和使用系统已有的OpenBLAS库
安装方法
在Cargo.toml中添加依赖:
[dependencies]
openblas-src = "0.10"
或者使用特定特性:
[dependencies.openblas-src]
version = "0.10"
features = ["cblas", "lapack"]
基本使用方法
1. 配置构建选项(可选)
在Cargo.toml中指定链接方式:
[package.metadata.openblas-src]
static = true # 静态链接
system = false # 不使用系统库
2. 基本矩阵运算示例
use openblas_src::*;
use std::os::raw::c_int;
fn main() {
// 定义矩阵和向量
let m = 3;
let n = 3;
let k = 3;
let alpha = 1.0;
let beta = 0.0;
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
let b = vec![9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0];
let mut c = vec![0.0; m * n];
// 执行矩阵乘法: C = α * A * B + β * C
unsafe {
dgemm_(
b'N' as *const i8, // transa
b'N' as *const i8, // transb
&m, // m
&n, // n
&k, // k
&alpha, // alpha
a.as_ptr(), // a
&m, // lda
b.as_ptr(), // b
&k, // ldb
&beta, // beta
c.as_mut_ptr(), // c
&m // ldc
);
}
println!("矩阵乘法结果: {:?}", c);
}
3. 线性方程组求解示例
use openblas_src::*;
use std::os::raw::c_int;
fn solve_linear_system() {
// 系数矩阵 A
let mut a = vec![
1.0, 2.0, 3.0,
4.0, 5.0, 6.0,
7.0, 8.0, 10.0
];
// 右侧向量 b
let mut b = vec![1.0, 2.0, 3.0];
let n = 3;
let mut ipiv = vec![0; n as usize];
let mut info = 0;
unsafe {
// LU分解
dgetrf_(
&n, // m
&n, // n
a.as_mut_ptr(), // a
&n, // lda
ipiv.as_mut_ptr(), // ipiv
&mut info // info
);
// 求解方程组
dgetrs_(
b'N' as *const i8, // trans
&n, // n
&1, // nrhs
a.as_ptr(), // a
&n, // lda
ipiv.as_ptr(), // ipiv
b.as_mut_ptr(), // b
&n, // ldb
&mut info // info
);
}
println!("解向量: {:?}", b);
}
完整示例代码
// 完整的openblas-src使用示例
use openblas_src::*;
use std::os::raw::c_int;
fn main() {
println!("=== 矩阵乘法示例 ===");
matrix_multiplication_example();
println!("\n=== 线性方程组求解示例 ===");
linear_system_solution_example();
println!("\n=== 向量点积示例 ===");
dot_product_example();
}
fn matrix_multiplication_example() {
// 定义3x3矩阵
let m = 3;
let n = 3;
let k = 3;
let alpha = 1.0;
let beta = 0.0;
// 矩阵A: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
// 矩阵B: [[9, 8, 7], [6, 5, 4], [3, 2, 1]]
let b = vec![9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0];
// 结果矩阵C
let mut c = vec![0.0; m * n];
// 执行矩阵乘法: C = α * A * B + β * C
unsafe {
dgemm_(
b'N' as *const i8, // transa: 不转置A
b'N' as *const i8, // transb: 不转置B
&m, // m: A的行数
&n, // n: B的列数
&k, // k: A的列数/B的行数
&alpha, // alpha: 标量系数
a.as_ptr(), // a: 矩阵A的数据指针
&m, // lda: A的前导维度
b.as_ptr(), // b: 矩阵B的数据指针
&k, // ldb: B的前导维度
&beta, // beta: 标量系数
c.as_mut_ptr(), // c: 结果矩阵C的数据指针
&m // ldc: C的前导维度
);
}
println!("矩阵A: {:?}", a);
println!("矩阵B: {:?}", b);
println!("矩阵乘法结果 C = A * B: {:?}", c);
}
fn linear_system_solution_example() {
// 系数矩阵 A: [[1, 2, 3], [4, 5, 6], [7, 8, 10]]
let mut a = vec![
1.0, 2.0, 3.0,
4.0, 5.0, 6.0,
7.0, 8.0, 10.0
];
// 右侧向量 b: [1, 2, 3]
let mut b = vec![1.0, 2.0, 3.0];
let n = 3;
let mut ipiv = vec![0; n as usize]; // 置换索引数组
let mut info = 0; // 执行信息
unsafe {
// LU分解: 将矩阵A分解为下三角矩阵L和上三角矩阵U
dgetrf_(
&n, // m: 矩阵行数
&n, // n: 矩阵列数
a.as_mut_ptr(), // a: 矩阵数据指针
&n, // lda: 前导维度
ipiv.as_mut_ptr(), // ipiv: 置换索引
&mut info // info: 执行信息
);
if info != 0 {
println!("LU分解失败,错误代码: {}", info);
return;
}
// 求解方程组: A * x = b
dgetrs_(
b'N' as *const i8, // trans: 不转置
&n, // n: 矩阵阶数
&1, // nrhs: 右侧向量数量
a.as_ptr(), // a: LU分解后的矩阵
&n, // lda: 前导维度
ipiv.as_ptr(), // ipiv: 置换索引
b.as_mut_ptr(), // b: 右侧向量/解向量
&n, // ldb: 前导维度
&mut info // info: 执行信息
);
}
if info != 0 {
println!("方程组求解失败,错误代码: {}", info);
} else {
println!("解向量 x: {:?}", b);
println!("验证: A * x 应该等于原始b向量");
}
}
fn dot_product_example() {
// 向量点积示例 (BLAS Level 1)
let n = 5;
// 向量x
let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
// 向量y
let y = vec![5.0, 4.0, 3.0, 2.0, 1.0];
// 计算点积: result = x · y
let result = unsafe {
ddot_(
&n, // n: 向量长度
x.as_ptr(), // x: 第一个向量
&1, // incx: x的增量
y.as_ptr(), // y: 第二个向量
&1 // incy: y的增量
)
};
println!("向量 x: {:?}", x);
println!("向量 y: {:?}", y);
println!("点积 x · y = {}", result);
}
性能优化建议
- 矩阵布局:使用列优先存储以获得最佳性能
- 线程配置:设置环境变量控制线程数:
export OPENBLAS_NUM_THREADS=4
- 内存对齐:确保数据内存对齐以提高缓存效率
注意事项
- 函数调用需要使用unsafe块
- 需要手动管理内存和数组维度
- 建议封装安全接口供业务代码使用
- 在Windows上可能需要安装额外的构建工具
替代方案
如果觉得openblas-src的API过于底层,可以考虑使用以下高级封装:
- ndarray-linalg
- rusty-machine
- nalgebra
openblas-src为Rust提供了接近原生性能的线性代数计算能力,特别适合对性能要求极高的科学计算场景。