用于记录阅读分析OpenBLAS源代码的各种知识点,防止遗忘。这里主要记录OpenBLAS的代码结构,因为确实比较复杂,直接看源代码很可能比较蒙比,如果知道其结构,看起来就比较轻松了。至于OpenBLAS矩阵乘法的算法,这篇不涉及,我会在另一篇文章中简单(瞎jb)分析。
OpenBLAS代码总体上可以分成三个层次:
1.接口层
在OpenBLAS接口层中,运算又分为三个类型,分别是level1到3,其中level3对应矩阵和矩阵的运算,level2和level1依次维度越来越低。不过这些level1~3都是BLAS内部计算时使用的接口(源代码基本在driver/level下),对外界用户的接口是不涉及这个概念的(对外接口基本都在interface文件夹下):
每一个源代码文件对应一种操作,如这里的gemm指的是普通矩阵乘法(General Matrix Multiplication)而gemv指的是普通矩阵向量乘法(General Matrix Vector)。打开gemm.c可以大致观察一下其源代码(大幅度阉割版):
//后边函数体中要使用的函数表,是计算矩阵乘法的核心函数,有这么多不同的函数指针,是区分了大量特殊情况,如GEMM_NN是两个普通矩阵相乘,而GEMM_TN说明第一个矩阵是转置过的,而带THREAD标签的是多线程实现的核心函数,其执行效率是单核执行的若干倍。
static int (*gemm[])(blas_arg_t *, BLASLONG *, BLASLONG *, FLOAT *, FLOAT *, BLASLONG) = {
#ifndef GEMM3M
GEMM_NN, GEMM_TN, GEMM_RN, GEMM_CN,
GEMM_NT, GEMM_TT, GEMM_RT, GEMM_CT,
GEMM_NR, GEMM_TR, GEMM_RR, GEMM_CR,
GEMM_NC, GEMM_TC, GEMM_RC, GEMM_CC,
#if defined(SMP) && !defined(USE_SIMPLE_THREADED_LEVEL3)
GEMM_THREAD_NN, GEMM_THREAD_TN, GEMM_THREAD_RN, GEMM_THREAD_CN,
GEMM_THREAD_NT, GEMM_THREAD_TT, GEMM_THREAD_RT, GEMM_THREAD_CT,
GEMM_THREAD_NR, GEMM_THREAD_TR, GEMM_THREAD_RR, GEMM_THREAD_CR,
GEMM_THREAD_NC, GEMM_THREAD_TC, GEMM_THREAD_RC, GEMM_THREAD_CC,
#endif
#else
GEMM3M_NN, GEMM3M_TN, GEMM3M_RN, GEMM3M_CN,
GEMM3M_NT, GEMM3M_TT, GEMM3M_RT, GEMM3M_CT,
GEMM3M_NR, GEMM3M_TR, GEMM3M_RR, GEMM3M_CR,
GEMM3M_NC, GEMM3M_TC, GEMM3M_RC, GEMM3M_CC,
#if defined(SMP) && !defined(USE_SIMPLE_THREADED_LEVEL3)
GEMM3M_THREAD_NN, GEMM3M_THREAD_TN, GEMM3M_THREAD_RN, GEMM3M_THREAD_CN,
GEMM3M_THREAD_NT, GEMM3M_THREAD_TT, GEMM3M_THREAD_RT, GEMM3M_THREAD_CT,
GEMM3M_THREAD_NR, GEMM3M_THREAD_TR, GEMM3M_THREAD_RR, GEMM3M_THREAD_CR,
GEMM3M_THREAD_NC, GEMM3M_THREAD_TC, GEMM3M_THREAD_RC, GEMM3M_THREAD_CC,
#endif
#endif
};
//gemm的函数体
void CNAME(enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE TransA, enum CBLAS_TRANSPOSE TransB,
blasint m, blasint n, blasint k,
#ifndef COMPLEX
FLOAT alpha,
#else
FLOAT *alpha,
#endif
FLOAT *a, blasint lda,
FLOAT *b, blasint ldb,
#ifndef COMPLEX
FLOAT beta,
#else
FLOAT *beta,
#endif
FLOAT *c, blasint ldc) {
//大量代码实现,主要是对输入矩阵格式进行操作,并且选择正确分支,最后调用上边函数表中的一个函数完成运算
。
。
。
。
//调用函数