跳到主要内容

Arm Performance Libraries替代Eigen的后端

在将 Eigen 的后端替换为 Arm Performance Libraries(APL,Arm 性能库,包含 Arm BLAS、LAPACK 等线性代数优化实现)时,核心思路是让 Eigen 调用 APL 提供的经过 Arm 架构优化的底层线性代数函数(如矩阵乘法、分解等),而非 Eigen 自带的通用实现,从而利用 APL 针对 Arm CPU(如 A55 等)的指令集(如 NEON)和架构特性优化,提升计算性能。以下是具体步骤和注意事项:

一、原理:Eigen 对外部 BLAS/LAPACK 的支持

Eigen 本身是一个纯头文件库,其底层线性代数运算默认使用自身的手写实现(部分优化了 NEON 指令),但也支持通过 接口适配 调用外部优化的 BLAS(基础线性代数子程序)和 LAPACK(线性代数包)库(如 APL、OpenBLAS、MKL 等)。
当 Eigen 检测到外部 BLAS/LAPACK 时,会优先调用这些库的高效实现(尤其是大矩阵运算,如 gemm 矩阵乘法、gesvd 奇异值分解等),从而提升性能。

二、具体步骤:用 APL 替代 Eigen 后端

1. 安装 Arm Performance Libraries

首先需要在目标 Arm 平台(如 A55 架构的边缘设备)上安装 APL。

  • 下载:从 Arm 官网获取适用于 Arm 64 位(aarch64)的 APL 安装包(需注册,Arm 开发者官网)。
  • 安装:按官方指南安装,默认路径通常为 /opt/arm/armpl/<版本号>/,包含头文件(include/)和库文件(lib/,如 libarmpl_lp64.so 等,lp64 对应 64 位模式)。

2. 配置 Eigen 以启用外部 BLAS/LAPACK 支持

Eigen 通过宏定义控制是否启用外部 BLAS/LAPACK,需在编译时指定相关宏和链接选项。

(1)修改 Eigen 配置(或编译宏)

在代码中 包含 Eigen 头文件前 定义以下宏,或在编译命令中通过 -D 传入:

cpp

// 启用 Eigen 对外部 BLAS 的支持
#define EIGEN_USE_BLAS
// 启用 Eigen 对外部 LAPACK 的支持(如需矩阵分解等高级功能)
#define EIGEN_USE_LAPACK

这些宏会让 Eigen 在编译时优先调用外部 BLAS(如 APL 的 cblas_* 函数)和 LAPACK(如 lapack_* 函数)。

(2)编译时链接 APL 库

编译代码时,需要指定 APL 的头文件路径、库路径,并链接 APL 的 BLAS/LAPACK 库。
以 g++ 为例,编译命令需包含:

bash

# 头文件路径(APL 的 include 目录)
-I/opt/arm/armpl/<版本号>/include
# 库文件路径(APL 的 lib 目录)
-L/opt/arm/armpl/<版本号>/lib
# 链接 APL 的 BLAS/LAPACK 库(根据 APL 版本可能需要调整库名)
-larmpl_lp64 -larmpl_lp64_mp # 64位模式的库,具体名称以安装目录为准
# 若 APL 依赖其他库(如 pthread),需补充链接
-lpthread -lm

3. 验证是否成功调用 APL

替换后需验证 Eigen 是否确实使用了 APL 的后端,避免仍使用 Eigen 自带实现:

  • 方法 1:编译日志检查
    编译时若出现类似 Eigen::blas::gemm 调用的提示(或无报错),说明宏定义生效。

  • 方法 2:性能对比
    运行包含大矩阵运算(如 matrixA * matrixBmatrix.jacobiSvd())的代码,对比替换前后的耗时。若 APL 适配正确,大矩阵运算性能应显著提升(小矩阵可能因调用开销差异不明显)。

  • 方法 3:工具调试
    使用 ldd 命令检查生成的可执行文件是否链接了 APL 的库(如 libarmpl_lp64.so):

    bash

    ldd your_executable | grep armpl

三、注意事项

  1. 适用场景
    APL 对 大矩阵运算(如维度 > 100 的矩阵乘法、SVD 分解)优化明显,而对小矩阵(如 VIO 中的 3x3 矩阵)可能不如 Eigen 自带的 NEON 优化高效(因函数调用开销占比高)。需结合实际算法(如 VINS-Fusion 中的滑窗优化、Fast-LIO 中的状态估计)的矩阵规模判断收益。

  2. 数据类型兼容性
    Eigen 默认使用列主序(Column-major)存储,与 BLAS 标准一致,无需调整数据布局;但需确保 Eigen 的数据类型(如 float/double)与 APL 编译时的精度(lp64 对应 64 位整数 + 32 位 float/64 位 double)匹配。

  3. 部分函数不支持
    Eigen 中的部分高级功能(如稀疏矩阵、自定义算子)可能不调用外部 BLAS/LAPACK,仍需依赖 Eigen 自身实现,需针对性优化(如手动调用 APL 接口)。

  4. 与其他优化结合
    替换后端后,可配合其他优化(如编译器 -O3 -march=armv8.2-a+neon 选项、OpenMP 多线程并行)进一步提升性能(APL 本身也支持多线程,可通过环境变量 ARMPL_NUM_THREADS 控制线程数)。

  5. 版本兼容性
    确保 Eigen 版本(建议 3.3 以上)支持外部 BLAS/LAPACK,且 APL 版本与目标 Arm 架构(如 A55 属于 Armv8.2-A)匹配,避免指令集不兼容(如 APL 编译时启用了 A55 不支持的指令)。

四、替代方案:直接调用 APL 接口

若 Eigen 对 APL 的适配不够理想,可在关键计算模块(如 VIO 中的协方差矩阵更新、LIO 中的点云配准)直接调用 APL 的 C 接口(如 cblas_sgemm 进行单精度矩阵乘法),跳过 Eigen 中间层,示例:

cpp

#include "armpl.h"  // APL 的头文件
// 计算 C = alpha*A*B + beta*C(A: MxK, B: KxN, C: MxN,列主序)
cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans,
M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);

通过以上方法,可让 Arm 架构的边缘设备(如 8 核 A55)在路径规划、VIO、LIO 等算法的线性代数密集型任务中,充分利用 APL 的硬件优化,提升运行效率。