---
title: Eigen库使用KML
description: "Eigen库是一个用于线性代数运算的C++模板库。它提供了高性能的矩阵运算和线性代数操作，被广泛应用于科学计算、机器学习和计算机图形学等领域。详细介绍可参考Eigen官网(https://eigen.tuxfamily.org/index.php?title=Main_Page)。Eigen库官方提供了使能开源BLAS以及LAPACK的方式，同样可以使用到KML中的BLAS与LAPACK库，使用说明详见Using BLAS/LAPACK from Eigen(https://libeigen.gitlab.io/eigen/docs-nightly/TopicUsingBlasLapack.html)，使用外部BLAS为Eigen提供了更高性能的矩阵运算接口。除了标准的BLAS、LAPACK接口，鲲鹏数学库提供了额外的BLAS接口供Eigen使用，详细接口信息请见表1。"
url: https://www.hikunpeng.com/document/detail/zh/kunpenghpcs/hpckit/mg/kunpengaccel_12_0067.html
sourcePath: /source/zh/kunpenghpcs/hpckit/mg/kunpengaccel_12_0067.html
indexId: 30a072a14cfa033c3984d7fcc7215c276af4f768f75d7d768e0646d5adb6757e58
---
# Eigen库使用KML

#### Eigen库介绍

Eigen库是一个用于线性代数运算的C++模板库。它提供了高性能的矩阵运算和线性代数操作，被广泛应用于科学计算、机器学习和计算机图形学等领域。详细介绍可参考Eigen官网(https://eigen.tuxfamily.org/index.php?title=Main_Page)。Eigen库官方提供了使能开源BLAS以及LAPACK的方式，同样可以使用到KML中的BLAS与LAPACK库，使用说明详见Using BLAS/LAPACK from Eigen(https://libeigen.gitlab.io/eigen/docs-nightly/TopicUsingBlasLapack.html)，使用外部BLAS为Eigen提供了更高性能的矩阵运算接口。除了标准的BLAS、LAPACK接口，鲲鹏数学库提供了额外的BLAS接口供Eigen使用，详细接口信息请见表1。


#### Eigen库KML优化适配

KML库在EIGEN_USE_BLAS机制基础上，进一步扩展并优化了多类Eigen接口，以提升应用场景下的整体性能。优化后的Eigen版本获取地址：kunpeng-eigen(https://gitee.com/kunpeng-hpc/eigen)。

对于矩阵加法等基础算子，其使用方式与原生 Eigen 接口保持一致，仅需在编译阶段将宏定义 -DEIGEN_USE_BLAS 替换为 -DKUNPENG_USE 即可启用相应优化路径。针对其他已优化接口的具体使用方法以及验证流程，将在后续章节中进行详细说明。

下表给出了当前支持优化的 Eigen 功能范围。


**表1 Eigen库KML优化接口说明**

| 接口名称 | 示例用法 | 适用数据类型 | KML优化接口或方式 |
| --- | --- | --- | --- |
| 矩阵加法 | C = A + B C += A | float double std::complex<float> std::complex<double> | ?omatadd ?imatadd |
| 矩阵减法 | C = A \- B C \-= A | float double std::complex<float> std::complex<double> | ?omatsub ?imatsub |
| 逐元素乘 | C = A.cwiseProduct(B) | float double std::complex<float> std::complex<double> | ?omatmul |
| 按行/列求和 | v = A.rowwise().sum() v = A.colwise().sum() | float double std::complex<float> std::complex<double> | ?gemv |
| 连续子块拷贝 | B = A.block(100, 200, 200, 300); | float double std::complex<float> std::complex<double> | ?copy |
| 逐元素拷贝 | B = A.array() | float double std::complex<float> std::complex<double> | ?copy |
| 激活函数 | B = A.tanh() | float | SVE intrinsic kernel |
| 张量收缩 | C=A.contract(B,dims) | 2d float | sgemm |


#### Eigen库TensorContraction使能KML

针对Eigen中的TensorContraction模块，在数据类型为float的二维张量收缩计算时，保持Eigen的多线程调度框架(ThreadPool)，替换底层算子，使能KML加速。

1. 获取优化后Eigen库。下载地址：kunpeng-eigen(https://gitee.com/kunpeng-hpc/eigen)。
2. 进入eigen/eigen_blas目录下，此处为Eigen对接KML适配层，独立编译，以二进制方式连接到Eigen，编译命令：

```
clang -shared -fPIC -o libeigen_blas.so eigen_blas.c -L${PATH_TO_KBLAS} -lkblas
```


  其中，PATH_TO_KBLAS需替换成对应的KML动态库路径，编译完成后生成libeigen_blas.so动态库。

3. Eigen根目录下，设置环境变量OMP_NUM_THREADS=1，KUNPENG_OPT_CONTRACT=1，KUNPENG_CONTRACT_NUM_THREADS=目标线程数，再执行编译命令。
  示例代码matmul_test.cpp：

```
#include <iostream>
#include <chrono>
#include <Eigen/Core>
#include <unsupported/Eigen/CXX11/Tensor>
#include <unsupported/Eigen/CXX11/ThreadPool>
using namespace Eigen;
int main() {
int M = 5000, N = 1024, K = 1024;
Tensor<float, 2, RowMajor> t_a(M, K);
Tensor<float, 2, RowMajor> t_b(K, N);
Tensor<float, 2, RowMajor> t_c(M, N);
t_a.setRandom();
t_b.setRandom();
Eigen::array<Eigen::IndexPair<int>, 1> product_dims = { Eigen::IndexPair<int>(1, 0) };
int num_threads = 4;
Eigen::ThreadPool pool(num_threads);
Eigen::ThreadPoolDevice device(&pool, num_threads);
unsetenv("KUNPENG_OPT_CONTRACT");
t_c.device(device) = t_a.contract(t_b, product_dims);
auto start_ori = std::chrono::high_resolution_clock::now();
t_c.device(device) = t_a.contract(t_b, product_dims);
auto end_ori = std::chrono::high_resolution_clock::now();
auto dur_ori = std::chrono::duration_cast<std::chrono::microseconds>(end_ori - start_ori).count();
setenv("KUNPENG_OPT_CONTRACT", "1", 1);
setenv("KUNPENG_CONTRACT_NUM_THREADS", "4", 1);
t_c.device(device) = t_a.contract(t_b, product_dims);
auto start_opt = std::chrono::high_resolution_clock::now();
t_c.device(device) = t_a.contract(t_b, product_dims);
auto end_opt = std::chrono::high_resolution_clock::now();
auto dur_opt = std::chrono::duration_cast<std::chrono::microseconds>(end_opt - start_opt).count();
std::cout << "--- MatMul (Contraction) KML Optimization Test ---" << std::endl;
std::cout << "Scale          : " << M << "x" << N << "x" << K << std::endl;
std::cout << "Original Time  : " << dur_ori << " us" << std::endl;
std::cout << "Optimized Time : " << dur_opt << " us" << std::endl;
std::cout << "Speedup        : " << (float)dur_ori / dur_opt << "x" << std::endl;
return 0;
}
```


  编译命令：

```
clang++ matmul_test.cpp -I ./eigen -I ./eigen/eigen_blas -o matmul_test -march=armv9-a+sve -DEIGEN_USE_THREADS -L ./eigen/eigen_blas -leigen_blas
```


  成功输出如下所示：

```
--- MatMul (Contraction) KML Optimization Test ---
Scale          : 1024x1024x1024
Original Time  : 2131122 us
Optimized Time : 5400 us
Speedup        : 394.652x
```


#### Eigen库tanh优化

在ARM架构上，Eigen tanh函数默认使用NEON指令集进行向量化处理。相比之下，SVE（可伸缩矢量扩展）支持128位到2048位的可变长度向量，可以一次性处理更多的tanh计算来提升性能。因此，在tanh的计算过程中，针对float数据类型，增加SVE intrinsic优化实现，以提升计算效率。

1. 获取优化后Eigen库。下载地址：kunpeng-eigen(https://gitee.com/kunpeng-hpc/eigen)。
2. 进入eigen根目录下，编译以下示例测试代码：
  tanh_test.cpp

```
#include <iostream>
#include <chrono>
#include <vector>
#include <Eigen/Core>
#include <unsupported/Eigen/CXX11/Tensor>
#include <unsupported/Eigen/CXX11/ThreadPool>
using namespace Eigen;
int main() {
int M = 500, N = 500;
int size = M * N;
std::vector<float> input_raw(size);
std::vector<float> output_raw(size);
for(int i = 0; i < size; ++i) input_raw[i] = static_cast<float>(rand());
TensorMap<Tensor<float, 2, RowMajor>> input_map(input_raw.data(), M, N);
TensorMap<Tensor<float, 2, RowMajor>> output_map(output_raw.data(), M, N);
Eigen::ThreadPool pool(1);
Eigen::ThreadPoolDevice device(&pool, 1);
auto start_ori = std::chrono::high_resolution_clock::now();
output_map.device(device) = (1.0f * input_map).tanh();
auto end_ori = std::chrono::high_resolution_clock::now();
auto dur_ori = std::chrono::duration_cast<std::chrono::microseconds>(end_ori - start_ori).count();
auto start_opt = std::chrono::high_resolution_clock::now();
output_map.device(device) = input_map.tanh();
auto end_opt = std::chrono::high_resolution_clock::now();
auto dur_opt = std::chrono::duration_cast<std::chrono::microseconds>(end_opt - start_opt).count();
std::cout << "--- Tanh SVE Optimization ---" << std::endl;
std::cout << "Original Time  : " << dur_ori << " us" << std::endl;
std::cout << "Optimized Time : " << dur_opt << " us" << std::endl;
std::cout << "Speedup        : " << (float)dur_ori / dur_opt << "x" << std::endl;
return 0;
}
```


  编译命令：

```
clang++ test_tanh.cpp -I ./eigen -o test_tanh -march=armv9-a+sve -I ./eigen/eigen_blas -DEIGEN_USE_THREADS
```


  执行后输出如下所示：

```
--- Tanh SVE Optimization ---
Original Time  : 43847 us
Optimized Time : 3987 us
Speedup        : 10.9975x
```
