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 编译器(例如 gccclangicc),
  • make
  • 目标架构的 CC 编译器(例如,对于 aarch64 使用 aarch64-linux-gnu-gcc),
  • 目标架构的 Fortran 编译器(例如 gfortranflangifort), 如果未检测到 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_proxyhttps_proxy,以下载必要的依赖项。

通过 OpenBLAS 的构建系统

根据 OpenBLAS 构建系统,OpenBLAS 使用的变量可以通过环境传递,例如 DYNAMIC_LISTNUM_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_CCOPENBLAS_FCOPENBLAS_HOSTCCOPENBLAS_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_CCOPENBLAS_HOSTCC,将使用 cc crate 来检测编译器。更多信息请参考 cc 文档。

对于 OPENBLAS_FCopenblas-build 将尝试通过上面设置的 OPENBLAS_CC 检测编译器。它将把 gcc 替换为 gfortranclang 替换为 flangicc 替换为 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);
}

1 回复

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);
}

性能优化建议

  1. 矩阵布局:使用列优先存储以获得最佳性能
  2. 线程配置:设置环境变量控制线程数:
    export OPENBLAS_NUM_THREADS=4
    
  3. 内存对齐:确保数据内存对齐以提高缓存效率

注意事项

  • 函数调用需要使用unsafe块
  • 需要手动管理内存和数组维度
  • 建议封装安全接口供业务代码使用
  • 在Windows上可能需要安装额外的构建工具

替代方案

如果觉得openblas-src的API过于底层,可以考虑使用以下高级封装:

  • ndarray-linalg
  • rusty-machine
  • nalgebra

openblas-src为Rust提供了接近原生性能的线性代数计算能力,特别适合对性能要求极高的科学计算场景。

回到顶部