centos OpenBLAS

来源:C 语言 / 时间:2026-09-28

OpenBLAS 是高性能多核BLAS库 是GotoBLAS2 1.13 BSD版本的衍生版 Lapack 是矩阵线性运算库 实现多种矩阵的运算 底层使用BLAS
OpenBLAS 详细介绍
https://github.com/xianyi/OpenBLAS/wiki/Faq
https://github.com/xianyi/OpenBLAS/wiki/User-Manual

环境的安装 权限不够,用在root权限下安装
Centos需要安装gcc及g++
安装gcc
yum install gcc

安装g++
yum install gcc-c++

查看gcc版本
gcc -v

建议> gcc4.8
gcc version 11.5.0 20240719 (Red Hat 11.5.0-15) (GCC)

矩阵运算在科学计算 机器学习和高性能计算领域占据了大量计算任务的核心
BLAS(Basic Linear Algebra Subprograms)作为矩阵运算的基础库
在大多数的高性能计算场景中出现
OpenBLAS 是一个开源 跨平台的 BLAS 库实现 因其高效性和广泛适用性而备受各界关注

OpenBLAS 源代码 帮助理解矩阵运算的底层实现
掌握高性能计算中的优化技巧(如多线程优化 SIMD 指令优化等)


一 安装 OpenBLAS
第一步
克隆OpenBLAS的源代码仓库
git clone https://github.com/OpenMathLib/OpenBLAS.git
cd OpenBLAS

第二步安装依赖
sudo dnf update && sudo dnf install build-essential cmake liblapack-dev libblas-dev

第三步 构建&&安装OpenBLAS
make
sudo make PREFIX=/data/c/openblas install

修改环境路径(两种方式)
方式 /etc/profile
vi /etc/profile

加入环境变量
#默认安装opt
LD_LIBRARY_PATH=/opt/OpenBLAS/lib

#自定义安装文件夹
LD_LIBRARY_PATH=/data/c/openblas/lib
export LD_LIBRARY_PATH


方式2
1.修改 LD_LIBRARY_PATH


vi ~/.bashrc


文件末尾添加
export LD_LIBRARY_PATH=/data/c/openblas/lib:$LD_LIBRARY_PATH

更新环境变量
source ~/.bashrc


二 理解BLAS与OpenBLAS的基本概念

2.1 BLAS的分层

Level 1: 向量运算(如点积 向量加法)

Level 2: 矩阵-向量运算(如矩阵向量乘法)

Level 3: 矩阵-矩阵运算(如矩阵乘法)

2.2 OpenBLAS的特点
多平台支持
支持 Linux Windows macOS 等多种操作系统

多线程优化
通过 pthread 或 OpenMP 实现多线程加速

SIMD 指令优化
利用 SSE AVX 等 SIMD 指令加速计算

动态加载优化内核
根据 CPU 类型动态加载最优内核

三 OpenBLAS 的项目结构分析

3.1 主要目录

OpenBLAS 的源代码仓库包含以下几个主要目录

include/
存储头文件(如 blas.h 和 cblas.h)

src/
存储源代码文件

tests/
存储测试用例

config/
存储配置文件

3.2 关键文件

blas.h
定义 BLAS 函数接口

cblas.h
定义 C 接口(CBLAS)

kernel/
存储高度优化的内核函数(如矩阵乘法的内核实现)

3.3 选择一个简单的函数进行研究
以 dgemm(双精度矩阵乘法)为例 这是 OpenBLAS 中最常用的函数之一
命令快速定位其实现
grep -r “dgemm_” src/

通常情况下 dgemm的实现位于src/dgemm.c或类似的文件中


四 深入分析 OpenBLAS 的核心功能

4.1 理解 dgemm 的实现
dgemm
双精度矩阵乘法函数 计算 C=αAB+βCC=αAB+βC 其中 AA 和 BB 是矩阵 CC 是结果矩阵
4.1.1 函数参数解析

void dgemm_(const char *TransA, const char *TransB,
const int *M, const int *N, const int K,
const double Alpha, const double A, const int lda,
const double B, const int ldb,
const double Beta, double C, const int ldc)


TransA 和 TransB
指示是否对矩阵 AA 和 BB 进行转置

M, N, K
矩阵的维度

Alpha 和 Beta
标量系数

A, B, C
输入和输出矩阵

lda, ldb, ldc
矩阵的行步长(leading dimension)

4.1.2 内部实现逻辑
dgemm 的实现通常分为以下几个步骤


参数检查
验证输入参数的有效性

内存分配
为临时变量分配内存

调用内核函数
根据 CPU 类型选择最优的内核函数(如 AVX 内核或 SSE 内核)

矩阵乘法计算
通过循环实现矩阵乘法的核心逻辑

五 OpenBLAS 的优化策略详解
5.1 多线程优化
OpenBLAS 通过多线程技术将计算任务分配到多个 CPU 核心上 从而提高计算效率
以下是 OpenBLAS 多线程优化的关键点


5.1.1 线程管理
OpenBLAS 使用 pthread 或 OpenMP 实现线程池管理
例如 在 src/thread.c 文件中可以看到线程池的实现逻辑


// 线程池初始化
pthread_mutex_init(&pool->lock, NULL);
pthread_cond_init(&pool->cond, NULL);
pool->active_threads = 0;
pool->max_threads = MAX_THREADS;
5.1.2 并行策略
OpenBLAS 将大矩阵分解为多个小块 分别由不同的线程处理
这种并行策略可以显著提高计算效率
例如 在 dgemm 的实现中 OpenBLAS 会根据矩阵大小动态调整线程数量


5.1.3 负载均衡
为了确保所有线程的工作负载均衡 OpenBLAS 采用了动态任务分配机制
每个线程会从任务队列中获取任务 直到所有任务完成


5.2 SIMD 指令优化
SIMD(Single Instruction, Multiple Data)指令允许 CPU 在单个周期内对多个数据元素进行相同的操作
OpenBLAS 通过 SIMD 指令大幅提升了计算性能


5.2.1 SIMD 指令类型
OpenBLAS 支持多种 SIMD 指令集 包括


SSE(Streaming SIMD Extensions)
AVX(Advanced Vector Extensions)
FMA(Fused Multiply-Add)
5.2.2 SIMD 优化示例
以下是一个基于 AVX 的矩阵乘法内核函数示例


void dgemm_avx(double C, const double A, const double B, int M, int N, int K, int lda, int ldb, int ldc) {
__m256d a_row0 = _mm256_load_pd(A);
__m256d a_row1 = _mm256_load_pd(A + lda);
__m256d b_col0 = _mm256_load_pd(B);
__m256d b_col1 = _mm256_load_pd(B + ldb);

__m256d c0 = _mm256_mul_pd(a_row0, b_col0);
__m256d c1 = _mm256_mul_pd(a_row1, b_col1);

_mm256_store_pd(C, _mm256_add_pd(c0, c1));
}
通过 SIMD 指令 OpenBLAS 可以在单个周期内完成多个浮点数运算 从而显著提升性能


5.3 缓存优化
现代 CPU 的缓存层次结构(如 L1 L2 L3 缓存)对性能有重要影响
OpenBLAS 通过以下方式优化缓存利用率


5.3.1 数据对齐
OpenBLAS 确保矩阵数据在内存中对齐到缓存行边界(通常是 64 字节)
这种对齐方式可以减少缓存未命中率


示例
内存布局与对齐

假设一个 4x4 的矩阵存储在内存中


double A[4][4] = {
{1.0, 2.0, 3.0, 4.0},
{5.0, 6.0, 7.0, 8.0},
{9.0, 10.0, 11.0, 12.0},
{13.0, 14.0, 15.0, 16.0}
};
内存地址对齐后 每个元素的起始地址都是 8 字节的倍数(因为double占据 8 字节)


5.3.2 阻塞技术
阻塞技术(Blocking Technique)是将大矩阵划分为小块(称为“块”或“tile”) 使得每个块可以完全放入 CPU 缓存中进行计算
例如 在 dgemm 的实现中 OpenBLAS 会将矩阵划分为多个小块 并逐块进行计算


示例
阻塞技术的应用

假设我们将一个大矩阵划分为多个 128x128 的小块


for (int i = 0; i < M; i += BLOCK_SIZE) {
for (int j = 0; j < N; j += BLOCK_SIZE) {
for (int k = 0; k < K; k += BLOCK_SIZE) {
// 计算子块之间的乘法
dgemm_block(C + iN + j, A + iK + k, B + kN + j,
BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
通过这种方式 每个子块都可以完全放入 CPU 缓存中 从而减少缓存未命中率


5.3.3 循环重排
OpenBLAS 通过重新排列循环顺序(Loop Reordering) 使得数据访问模式更符合缓存的局部性原理
例如 将外层循环改为按块访问数据


示例
循环重排

原始循环顺序


for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < K; k++) {
C[iN + j] += A[iK + k] * B[kN + j];
}
}
}
优化后的循环顺序


for (int i_block = 0; i_block < M; i_block += BLOCK_SIZE) {
for (int j_block = 0; j_block < N; j_block += BLOCK_SIZE) {
for (int k_block = 0; k_block < K; k_block += BLOCK_SIZE) {
// 计算子块之间的乘法
dgemm_block(C + i_blockN + j_block, A + i_blockK + k_block,
B + k_blockN + j_block, BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
通过按块访问数据 可以显著提高缓存利用率


5.4 动态调度优化
OpenBLAS 根据 CPU 类型动态加载最优内核函数
以下是动态调度优化的关键点


5.4.1 CPU 检测
OpenBLAS 在启动时会检测 CPU 类型和指令集支持情况
例如 在 src/init.c 文件中可以看到 CPU 检测逻辑


// 检测 CPU 是否支持 AVX 指令集
if (cpu_has_avx()) {
// 加载 AVX 优化的内核函数
load_avx_kernels();
} else if (cpu_has_sse()) {
// 加载 SSE 优化的内核函数
load_sse_kernels();
}
5.4.2 动态加载内核
根据 CPU 类型 OpenBLAS 动态加载最优的内核函数
这种方式确保了在不同硬件上的最佳性能表现


示例
动态加载内核

// 加载 AVX 优化的内核函数
void load_avx_kernels() {
dgemm_ = dgemm_avx;
// 加载其他 AVX 优化的函数
}

// 加载 SSE 优化的内核函数
void load_sse_kernels() {
dgemm_ = dgemm_sse;
// 加载其他 SSE 优化的函数
}
六 性能基准测试
为了验证OpenBLAS的性能优势 我们可以设计一系列基准测试实验


6.1 测试用例设计
6.1.1 不同规模的矩阵
测试不同规模的矩阵乘法(如 1000x1000 2000x2000 等) 观察 OpenBLAS 的性能表现


6.1.2 不同硬件配置
在不同硬件配置(如单核 CPU 多核 CPU 带 AVX 指令集的 CPU)上运行测试用例 验证 OpenBLAS 的多线程和 SIMD 优化效果


6.1.3 对比其他 BLAS 实现
将 OpenBLAS 的性能与 ATLAS Intel MKL 等其他 BLAS 实现进行对比 验证其优势


6.2 测试程序示例
以下是一个简单的测试程序 用于测量 dgemm 的性能


#include <stdio.h>
#include <time.h>
#include “blas.h”

#define BLOCK_SIZE 128

void dgemm_test(int M, int N, int K) {
double A = (double )malloc(MKsizeof(double));
double B = (double )malloc(KNsizeof(double));
double C = (double )malloc(MNsizeof(double));

// 初始化矩阵
for (int i = 0; i < MK; i++) A[i] = 1.0;
for (int i = 0; i < KN; i++) B[i] = 1.0;
for (int i = 0; i < M
N; i++) C[i] = 0.0;

// 记录开始时间
clock_t start = clock();

// 调用 dgemm
dgemm_(“N”, “N”, &M, &N, &K, &1.0, A, &M, B, &K, &0.0, C, &M);

// 记录结束时间
clock_t end = clock();
double time_spent = (double)(end - start) / CLOCKS_PER_SEC;

printf(“Matrix size: %dx%d x %dx%d\n”, M, K, K, N);
printf(“Time spent: %.6f seconds\n”, time_spent);

free(A);
free(B);
free©;
}

int main() {
dgemm_test(1000, 1000, 1000);
return 0;
}
6.3 性能分析
通过运行上述测试程序 我们可以得到以下结论


多线程优化
在多核 CPU 上运行时 OpenBLAS 的性能显著优于单线程实现

SIMD 指令优化
在支持 AVX 指令集的 CPU 上运行时 OpenBLAS 的性能进一步提升

缓存优化
通过阻塞技术和循环重排 OpenBLAS 显著减少了缓存未命中率 提高了整体性能

上一篇: YUM VS-DNF

下一篇: ggml 轻量模型推理库

客服