ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

OpenBLAS 开发者指南:源码架构、GEMM 优化、测试体系与 CPU 适配开发实战

OpenBLAS 开发者指南:源码架构、GEMM 优化、测试体系与 CPU 适配开发实战 高性能计算科学计算【免费下载链接】OpenBLASOpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version.项目地址https://gitcode.com/gh_mirrors/op/OpenBLAS点击查看免费下载本文是面向 OpenBLAS 开发者的技术手册围绕 docs/developers.md 展开系统梳理 OpenBLAS 的源码目录结构与dgemm调用链、为特定硬件优化 GEMM 的方法论、四套测试体系的运行方式、基准测试手段以及为新型号 CPU 甚至全新架构添加支持的完整开发流程。读完本文你将能够从源码层面定位 OpenBLAS 的性能内核掌握KERNEL.*/param.h/cpuid_*.c等关键文件的协作关系并具备独立开展内核优化、回归测试与 CPU 适配开发的能力。源码布局从目录结构读懂 OpenBLAS 的分层设计OpenBLAS 的源码按接口层 → 驱动层 → 内核层以及配套的测试、构建体系组织。顶层目录布局如下可在仓库根目录直接对照查看OpenBLAS/ ├── benchmark BLAS 基准测试代码 ├── cmake CMake 构建文件 ├── ctest CBLAS 接口测试代码 ├── driver C 语言实现的驱动层 │ ├── level2 二级 BLAS 驱动gemv、gbmv、sbmv、spmv、ger 等 │ ├── level3 三级 BLAS 驱动gemm、symm、syrk 等含 Goto 算法实现 │ ├── mapper │ └── others 内存管理、线程调度等 ├── exports 生成共享库 ├── interface 实现 BLAS 与 CBLAS 接口调用 driver 或 kernel │ ├── lapack │ └── netlib ├── kernel 针对各 CPU 架构优化的汇编内核 │ ├── alpha 面向 DEC Alpha 的原始 GotoBLAS 内核 │ ├── arm ARMV5/V6/V7 内核含供其他架构复用的通用 C 代码 │ ├── arm64 ARMV8 内核 │ ├── generic 纯 C 编写的通用内核代码部分被多种架构复用 │ ├── ia64 面向 Intel Itanium 的原始 GotoBLAS 内核 │ ├── mips / mips64 │ ├── power │ ├── riscv64 │ ├── simd 通用 Intrinsics 公共代码供部分 x86_64 与 arm64 内核使用 │ ├── sparc │ ├── x86 / x86_64 │ └── zarch ├── lapack 优化的 LAPACK 代码替换常规 LAPACK 中的对应实现 │ ├── getf2 / getrf / getrs │ ├── laswp / lauu2 / lauum │ ├── potf2 / potrf │ ├── trti2 / trtri / trtrs ├── lapack-netlib 来自 netlib 参考实现的 LAPACK 代码 ├── reference BLAS Fortran 参考实现当前未使用 ├── relapack Elmar Peise 的递归 LAPACK基于常规 LAPACK 实现 ├── test BLAS 测试代码 └── utest 回归测试regression test从源码结构可以清晰地看出 OpenBLAS 的三层设计哲学interface 层向用户暴露 Fortran 风格 BLAS/LAPACK 符号与 CBLAS 接口负责参数校验、错误处理xerbla随后把任务派发给 driver 或 kernel例如 interface/gemm.cdriver 层以 C 语言实现算法调度如 driver/level3/level3.c、driver/level2/gemv_thread.c 等负责分块、缓存复用与多线程调度kernel 层真正数指令的地方每个 CPU 型号的内核选择由kernel/$(ARCH)/KERNEL.$(CPU)文件声明例如 kernel/x86_64/KERNEL.HASWELL。追踪一条真实调用链以dgemm为例文档给出的dgemm调用树如下interface/gemm.c │ driver/level3/level3.c │ gemm assembly kernels at kernel/即用户调用dgemm时先进入 interface/gemm.c约 800 余行的 C 接口实现负责参数解析与阈值判断再进入 driver/level3/level3.c 执行 Goto 算法的主循环最终落到kernel/下与当前编译目标对应的汇编/C 内核上完成寄存器分块内的乘加运算。要确定某一款受支持 CPU 当前实际使用的是哪个内核文件只需查看对应的kernel/$(ARCH)/KERNEL.$(CPU)文件。以 kernel/x86_64/KERNEL.HASWELL 为例其中相关的几行是... DTRMMKERNEL dtrmm_kernel_4x8_haswell.c DGEMMKERNEL dgemm_kernel_4x8_haswell.S ...依据上述KERNEL.HASWELL配置OpenBLAS 的 Haswelldgemm内核文件为dgemm_kernel_4x8_haswell.S同一文件中还可见SGEMMKERNEL sgemm_kernel_8x4_haswell_2.c、ZGEMMKERNEL zgemm_kernel_4x2_haswell.c等对应不同精度的内核选择。4x8、8x4这类命名直接揭示了寄存器分块尺寸——例如dgemm_kernel_4x8表示每个寄存器分块覆盖 4 行 × 8 列的输出子矩阵。逐行阅读各架构的KERNEL.*文件是快速理解这个 CPU 上 OpenBLAS 到底跑的是什么代码的最直接方式。为给定硬件优化 GEMM算法、内核与参数调优先读懂 Goto 算法driver/level3/level3.c是 Goto 算法的实现。该算法的核心思想是通过精心设计的多层分块Mc、Kc、Nc与 packing打包最大化数据在寄存器、L1/L2/L3 缓存之间的复用从而在矩阵乘法中逼近理论峰值。深入理解该算法建议阅读 Kazushige Goto 与 Robert A. van de Geijn 于 2008 年发表的经典论文Anatomy of High-Performance Matrix MultiplicationACM Transactions on Mathematical Software 34(3): Article 12该论文及相关文献可在 van de Geijn 的 FLAME 项目主页获取。从朴素内核出发gemmkernel_2x2.c文档特别推荐阅读 kernel/generic/gemmkernel_2x2.c这是一个用纯 C 编写的朴素2x2寄存器分块 GEMM 内核是理解内核到底在做什么的最佳起点。从源码看它通过三重循环j列块、i行块、k缩减维度遍历矩阵维护res0、res1、res2、res3四个累加器对应一个 2×2 输出块内层以 4 步为单位展开k循环for (k0; kbk/4; k1)并在每个迭代里交替从ptrba打包后的 A与ptrbb打包后的 B加载元素做乘累加最后用(bk3)处理余数循环结束后用res0/res1/res2/res3更新输出矩阵 C含ALPHA缩放与累加语义。这个朴素版本的性能远不及汇编内核但它的结构寄存器累加器、循环展开、packing 后的顺序访问正是所有高性能 GEMM 内核的共性骨架。对照它可以清楚地看出后续优化要往哪些方向发力编写优化的汇编内核需要综合考虑指令流水线、可用寄存器数量、内存/缓存访问模式。例如dgemm_kernel_4x8_haswell.S这类汇编内核正是通过 AVX/AVX2 向量指令、更深的循环展开与精心编排的加载/计算交错来榨取吞吐。调优缓存分块尺寸Mc、Kc、Nc分块尺寸决定打包块在各级缓存中的驻留情况是影响内存带宽利用率的决定性参数。param.h中的参数并非全部活跃调优前先核对文档特别提醒param.h仓库根目录约 4400 余行中并非所有 CPU 相关参数都在算法中真正被使用。以实际源码为准可以验证DNUMOPT只作为 level3syrk接口代码性能分析输出中的一个比例因子出现而它的对应物SNUMOPT在 common.h 中被别名为NUMOPT根本没有任何地方使用。从 param.h 可以看到各 CPU 段确实定义了#define SNUMOPT 4/8/16、#define DNUMOPT 2/4/8等不同取值但它们对生成代码的语义影响极其有限。SYMV_P只被symv与chemv/zhemv的通用内核使用param.h 中典型取值如#define SYMV_P 16。而这些函数通常已被各 CPU 的专用实现覆盖因此如果你从某个相近 CPU 的现有实现克隆起步必须查看它的KERNEL文件确认调优SYMV_P是否真的会产生影响。GEMV_UNROLL只被部分较老的 x86-64 内核使用因此param.h中并非每个 CPU 段都定义它实测仅在部分 x86_64 段出现如#define GEMV_UNROLL 8。此外param.h中诸如 L2/L3 缓存大小之类的 CPU 参数也不一定被当前型号的内核实际使用——种种迹象表明 CPU 识别代码最初是从其他项目移植而来的。结论是在修改任何param.h参数前先全局搜索该参数在 kernel/driver 中的真实引用点再决定是否值得调优。运行 OpenBLAS 测试四套测试体系一览OpenBLAS 使用多套测试Netlib BLAS、CBLAS、LAPACK外加 OpenBLAS 特有的回归测试。用 Make 即可运行命令测试内容对应目录make -C testBLAS 测试Fortran*blat*.f*blat*.dat数据驱动testmake -C ctestCBLAS 接口测试c_*blas*.c/c_*blat*.f等ctestmake -C utestOpenBLAS 回归测试C 语言单元测试如 utest/test_amax.c、utest/test_axpy.c、utest/test_gemv.cutestmake lapack-testLAPACK 测试lapack-netlib 等例如 test 目录下可看到sblat1.f、dblat2.f、zblat3.f等 BLAS 测试程序及其数据文件sblat2.dat、dblat3.dat它们通过标准 BLAS 测试用例验证数值正确性ctest 目录则包含c_sblat1c.c、c_dblat2c.c等 CBLAS 测试。测试 Makefile如 test/Makefile中还会根据编译器的不同对 Fortran 优化标志做针对性调整如对 GFORTRAN 关闭部分向量化以保证测试判定的可靠性。除内置测试外OpenBLAS 还使用 BLAS-Tester即适配 OpenBLAS 构建的 ATLAS 测试套件进行回归验证并借助多个与 GitHub 联动的 CI 服务在大量平台与构建配置上自动运行测试。另外Julia、NumPy、SciPy、Octave、QuantumEspresso 这类数值密集型项目在链接 OpenBLAS 后其自带测试套件也可作为 OpenBLAS 的回归测试手段。基准测试本地基准、PR 级性能回归与跨架构基准OpenBLAS 的基准测试体系分三个层次本地单函数基准benchmark目录提供了一批用于单独测试各个 BLAS 函数的简单 C 基准程序如 benchmark/gemm.c、benchmark/dgemm.c 这类按操作分类的源码通过该目录下的Makefile即可本地运行benchmark/scripts 子目录则提供了经由 NumPyNUMPY、SciPySCIPY、OctaveOCTAVE和 RR调用 OpenBLAS 的同类基准脚本。PR 级性能回归检查在 pull request 上会选取一组代表性函数用 Codspeed 做性能回归测试结果可在其公开看板查看。跨架构定时基准OpenMathLib/BLAS-Benchmarks 仓库维护了一套基于 Airspeed Velocity (asv) 的基准套件通过 cron 任务在多种 CPU 架构上定时运行结果发布到公开仪表盘。此外BLAS 库基准代码与具体性能分析结果在社区中也有不少现成资源例如 MatlabJuliaMatrixOperationsBenchmarkJulia/Matlab 中的各类矩阵运算对比与 mmperf单核矩阵乘法基准均可作为评估与对比的参考。为受支持 CPU 的新版本/变体添加自动检测支持这一场景在 x86-64 上尤为常见某个新型号 CPU 可能只是现有家族内的刷新die shrink 和/或核数变化指令集并无显著改动。例如 Intel Skylake 与 Kaby Lake 在架构本质上仍与 Haswell 相同低端 Goldmont 等则仍属 Nehalem 范畴。此时用合适的较老TARGET直接编译通常就能得到令人满意的构建结果。若仍需实现新型号的自动检测需要在其所属大架构对应的cpuid_architecture.c中补充 CPUID或等价标识符处理并为新型号设置合适的返回名称。对 x86最复杂的cpuid文件即 cpuid_x86.c而言有两个函数需要修改get_cpuname()返回具体型号名例如CPUTYPE_HASWELLget_corename()返回更宽泛的核心家族名例如CORE_HASWELL。这两个返回值最终会进入getarch生成的Makefile.conf与config.h中。若任一设置缺失通常会导致构建后期GEMM_UNROLL参数缺失——因为getarch_2nd将无法在param.h中找到匹配的参数段。对于支持DYNAMIC_ARCH构建的架构运行时 CPU 检测的代码位于 driver/others 下x86 在dynamic.c其他架构在dynamic_arch.c。需要注意x86 的 CPUID 在比较前会被拆分为 family、extended family、model、extended model 四部分因此 Linux/proc/cpuinfo中返回的十进制 model 必须先转换回十六进制再拆分。例如142 8E对应 extended model 8、model 14。为新型号 CPU 添加专门支持dedicated support当现有TARGET无法满足新型号时可为其建立专门支持。文档给出的标准流程是克隆KERNEL配置通常从现有型号出发把它的KERNEL配置文件克隆为新型号名即新的TARGET名随后逐个替换为更适合新型号特性的内核实现补充GEMM_UNROLL参数在顶层 param.h 中添加或先克隆对应的GEMM_UNROLL参数段补充宏定义可能需要在 kernel 目录下的Makefile及CMakeLists.txt中添加诸如USE_TRMM之类的定义——该宏决定TRMM函数是使用对应的GEMM内核还是独立的源文件登记新名称把新 CPU 名称加入 TargetList.txt补充自动检测在cpuid_architecture.c中完成前述 CPUID或等价信息处理供getarch辅助程序使用。为全新架构添加支持为 OpenBLAS 添加一个全新架构文档建议的最佳起点是整体克隆 32 位 ARM 的支持结构尤其是其中的 ARMv5 CPU——因为 ARMv5 完全以纯 C 内核实现参见 kernel/arm 目录下的 ARMV5 相关代码与通用 C 代码不涉及汇编移植可显著降低起步门槛。从仓库结构看一个完整架构需要落地的购物清单包括kernel/arch/目录及KERNEL.*配置、Makefile.arch、common_arch.h、cpuid_arch.c、param.h中的参数段以及 TargetList.txt 的登记项等。可参考社区 pull request如 OpenMathLib/OpenBLAS#1526获取一份现成的核对清单样例。开发者速查关键文件与各自职责文件/目录职责interface/gemm.cBLAS 接口层参数校验后派发 driverdriver/level3/level3.cGoto 算法主循环分块与打包调度kernel/generic/gemmkernel_2x2.c纯 C 的 2×2 寄存器分块 GEMM 内核学习模板kernel/$(ARCH)/KERNEL.$(CPU)声明某 CPU 型号各函数实际使用的内核文件如 kernel/x86_64/KERNEL.HASWELLparam.h各 CPU 的调优参数GEMM_UNROLL、SYMV_P等注意部分参数并非活跃cpuid_arch.cCPU 自动识别如 cpuid_x86.cget_cpuname/get_corenamedriver/others/dynamic.cDYNAMIC_ARCH运行时 CPU 检测x86TargetList.txt受支持 CPU 目标清单test / ctest / utest分别对应 BLAS / CBLAS / 回归测试benchmark 与 benchmark/scriptsC 与脚本语言两套本地基准总而言之OpenBLAS 的开发工作高度依赖接口层 → 驱动层 → 内核层这条清晰的调用链以及KERNEL.*、param.h、cpuid_*.c三份关键配置文件之间的联动。无论是优化内核、调优分块参数、跑回归测试还是为新型号乃至新架构做适配都可以沿本文的路径在仓库中快速定位到对应源码做到改前查引用、改后跑测试。赞分享高性能计算科学计算【免费下载链接】OpenBLASOpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version.项目地址https://gitcode.com/gh_mirrors/op/OpenBLAS点击查看免费下载相关推荐Miller Go 实现开发者指南构建、测试、源码架构与性能优化实践Miller Go 实现开发者指南构建、测试、源码架构与性能优化实践 本篇技术指南面向想要理解或参与 Miller mlr Go 实现开发的工程师系统梳CLI数据分析Kong Gateway 开发者实战指南从源码构建、测试体系到 EmmyLua 断点调试Kong Gateway 开发者实战指南从源码构建、测试体系到 EmmyLua 断点调试 本文是 Kong Gateway本仓库即其核心源码开发者工作流的API网关后端LLM 网关微服务人工智能devenv 源码仓库开发指南Rust 工作区架构、测试体系与 Tracing 调试实战devenv 源码仓库开发指南Rust 工作区架构、测试体系与 Tracing 调试实战 本篇指南面向想在 devenv 开源仓库中阅读代码、参与构建或进行二开发工具CLI上一篇【亲测免费】 AndroidPicker 使用指南下一篇【亲测免费】 OpenHTMLtoPDF 开源项目教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表