- 科学计算
- 数据科学
- 高性能计算
【免费下载链接】scipy
SciPy library main repository
本指南以 SciPy 官方构建文档 doc/source/building/blas_lapack.rst 为骨架,系统讲解从源码构建 SciPy 时如何选择 BLAS/LAPACK 后端(OpenBLAS、MKL、Accelerate、BLIS、ATLAS、Netlib 等)、如何在非标准路径下借助 pkg-config 发现独立库文件、如何解决 g77/gfortran 两种 Fortran ABI 不兼容问题,以及 ILP64(64 位整数)构建与 Cython 层整数 ABI 的取舍。读完本文,你将掌握 SciPy 各类 BLAS/LAPACK 构建选项的准确写法、底层检测原理,以及下游 Cython 包如何适配blas_int/blas_bint类型以同时兼容 LP64 与 ILP64 构建。
构建选项总览:一切从 Meson 选项开始
SciPy 目前采用 Meson 构建系统,BLAS/LAPACK 的库选择(除默认的 OpenBLAS 外)全部通过 Mesonbuild options机制实现。所有相关选项集中定义在仓库根目录的 meson.options 文件中,其默认值如下:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
blas | string | openblas | 要链接的 BLAS 库 |
lapack | string | openblas | 要链接的 LAPACK 库 |
use-ilp64 | boolean | false | 是否使用 ILP64(64 位整数)BLAS/LAPACK 接口 |
cython-blas-abi | combo | auto | cython_blas/cython_lapackCython API 的整数 ABI,取值auto/lp64/ilp64 |
blas-symbol-suffix | string | auto | 使用的 BLAS/LAPACK 符号后缀 |
mkl-threading | string | auto | MKL 线程方式,可选seq/iomp/gomp/tbb |
use-g77-abi | boolean | false | 是否强制使用 g77 兼容包装器调用 LAPACK |
其中use-ilp64、cython-blas-abi、use-g77-abi三个选项正是本指南后续三个核心章节的构建开关,blas与lapack选项名直接对应构建命令行中的-Dblas=与-Dlapack=参数。
选择 BLAS 与 LAPACK 实现
开发构建与打包构建的两种写法
文档给出了开发构建(配合 SciPy 自带的spin工具)与打包发布(python -m build)两种场景下切换到纯libblas/liblapack的命令。这里说的"纯libblas"在 Linux 发行版上通常指 Netlib BLAS/LAPACK,而在 conda-forge 上可以通过链接器机制在同一套库名上动态切换不同实现:
# 开发构建(spin) $ spin build -S-Dblas=blas -S-Dlapack=lapack # 构建并安装 wheel $ python -m build -Csetup-args=-Dblas=blas -Csetup-args=-Dlapack=lapack $ pip install dist/scipy*.whl # 或者,pip>=23.1 起也可这样直接使用: $ python -m pip -Csetup-args=-Dblas=blas -Csetup-args=-Dlapack=lapack注意两种命令行前缀的区别:spin使用-S-Dblas=...(-S表示把选项透传给 Meson setup),而python -m build使用-Csetup-args=-Dblas=...(-C把参数传给 build 后端)。文档最后还给出了省略setup-args前缀直接写-C-Duse-g77-abi=true的用法,说明-Csetup-args=与-C均可接受。
各主流实现的选择参数
只要对应库以pkg-config或 CMake 方式安装到位,以下选项均可正常工作:
- Accelerate(macOS 系统框架):
-Dblas=accelerate - MKL:使用其对应 pkg-config 文件名,例如
-Dblas=mkl-dynamic-lp64-gomp - BLIS:
-Dblas=blis——注意 BLIS 只提供 BLAS,因此还需要同时给出-Dlapack=指向一个 LAPACK 实现 - ATLAS:
-Dblas=blas-atlas(具体 pkg-config 文件名可能随发行版而异)
由于 Accelerate、MKL 与 SciPy 官方配套的scipy-openblas都同时包含 BLAS 与 LAPACK 两套接口,且二者版本必然一致,因此这三种情况下无需再指定-Dlapack。例如仅用一条参数即可构建出基于 Accelerate 的 wheel:
$ python -m build -Csetup-args=-Dblas=acceleratespin 的便捷别名
对于日常开发最常用的两个后端,spin提供了更易记忆的专属开关:
$ spin build --with-accelerate $ spin build --with-scipy-openblas=32其中--with-scipy-openblas=32后面的数字对应scipy-openblas打包时的符号宽度(LP64),这是开发环境中最常见的选择之一。
使用 pkg-config 检测非标准位置的库
底层检测机制
文档明确指出,Meson 在后台对指定库的发现顺序是:先用pkg-config探测,再用 CMake 探测。因此当你手里只有一套散落的共享库文件时(例如armpl_lp64.so位于/a/random/path/lib/,对应头文件位于/a/random/path/include/),正确的做法不是去改 Meson 配置,而是手工编写一个 pkg-config 的.pc文件。
编写自己的 .pc 文件
.pc文件的名称必须与-Dblas=/-Dlapack=中使用的名字一致(本例为armpl_lp64.pc),位置则可以放在任意目录,通过环境变量PKG_CONFIG_PATH指向它。文件内容模板如下:
libdir=/path/to/library-dir # e.g., /a/random/path/lib includedir=/path/to/include-dir # e.g., /a/random/path/include version=1.2.3 # set to actual version extralib=-lm -lpthread -lgfortran # if needed, the flags to link in dependencies Name: armpl_lp64 Description: ArmPL - Arm Performance Libraries Version: ${version} Libs: -L${libdir} -larmpl_lp64 # linker flags Libs.private: ${extralib} Cflags: -I${includedir}验证 .pc 文件是否生效
编写完成后,用pkg-config的两个查询命令即可验证解析是否正确:
$ pkg-config --libs armpl_lp64 -L/path/to/library-dir -larmpl_lp64 $ pkg-config --cflags armpl_lp64 -I/path/to/include-dir若输出与预期一致,说明 SciPy 构建时也能通过这条PKG_CONFIG_PATH找到该库。
指定 Fortran ABI:g77 与 gfortran 的兼容问题
问题根源
部分线性代数库使用g77ABI(又称 "f2c calling convention")编译,另一些则使用 GFortran ABI,这两种 ABI 彼此不兼容。SciPy 默认按 GFortran ABI 构建,如果链接到用 g77 ABI 构建的库(MKL 是典型代表),运行时会抛出异常甚至直接段错误(segfault)。
SciPy 的解决方式
文档说明 SciPy 通过ABI 包装器(ABI wrappers)来解决:包装器依赖 CBLAS API,或针对 BLAS API 中少数受影响函数使用自定义包装。这一点可以从源码得到印证——仓库中的 scipy/_build_utils/_generate_blas_wrapper.py 注释说明这些包装器被硬编码在wrap_g77_abi.c与wrap_dummy_g77_abi.c中,用于处理 ABI 差异及缺失符号;对应实现位于 scipy/_build_utils/src/wrap_dummy_g77_abi.c,其注释同样说明wrap_g77_abi.c中的包装器通过调用对应接口来保证兼容性。
自动检测与手动覆盖
关键点在于:SciPy 必须在构建时就知道该走哪条路径。构建系统会自动检测目标库是否为 MKL 或 Accelerate——这两者恒为 g77 ABI——若命中则改用 CBLAS API 而非 BLAS API。当自动检测失效,或用户希望针对纯libblas/liblapack强制使用该机制时(conda-forge 就是这么做的),使用-Duse-g77-abi=true选项覆盖:
$ python -m build -C-Duse-g77-abi=true -Csetup-args=-Dblas=blas -Csetup-args=-Dlapack=lapack构建 ILP64(64 位整数)BLAS/LAPACK
构建开关与典型命令
以-Duse-ilp64=true开启 ILP64 支持。官方文档给出两个典型示例:
- macOS 上使用 Accelerate:
$ python -m build --wheel -Csetup-args=-Dblas=accelerate -C-Duse-ilp64=true -Dcython-blas-abi=lp64- x86-64 上使用 MKL:
# 注意 `-Dblas=` 参数中的 "lp64" 并非笔误;只要 cython_blas ABI 设置为 "lp64"(见下节),就必须如此 $ python -m build --wheel -Csetup-args=-Dblas=mkl-dynamic-lp64-seq -C-Duse-ilp64=true -Dcython-blas-abi=lp64-Dblas=mkl-dynamic-lp64-seq中的lp64表示链接 MKL 的 LP64 符号集(即符号本身是 32 位整数接口),而-Duse-ilp64=true会通过符号后缀或 CBLAS 层让 SciPy 内部拿到 ILP64 能力——这正是blas-symbol-suffix选项存在的意义。
对 Python 层 API 的影响
构建时开启-Duse-ilp64=true会默认同时把scipy.linalg中的底层 Python 与 Cython API 翻转为 ILP64,这可能导致下游用法需要相应适配。在 Python 层,底层 BLAS/LAPACK 函数可从scipy.linalg.blas与scipy.linalg.lapack两个命名空间获得(两者均已在 scipy/linalg/init.py 中导入并公开):
>>> from scipy.linalg.blas import dgemm # 可能是 LP64 或 ILP64 版本要显式选择某个底层例程的具体变体,使用get_blas_funcs与get_lapack_funcs两个选择函数。其实现位于 scipy/linalg/blas.py 与 scipy/linalg/lapack.py(均带_memoize_get_funcs记忆化装饰器以缓存查找结果),ilp64参数接受True/False/'preferred'三种取值,其中'preferred'表示"优先返回 ILP64 例程,若不可用则回退到 32 位 LP64 例程",默认即为'preferred'。返回函数的int_dtype属性记录了整型参数的实际位宽:
>>> from scipy.linalg.blas import get_blas_funcs >>> daxpy = get_blas_funcs('axpy', (np.ones(3),), ilp64='preferred') >>> daxpy.int_dtype dtype('int64') # depends on the build option高层线性代数函数(norm、solve等)在内部正是通过这套机制选择例程,因此对 LP64/ILP64 是透明兼容的,用户无需关心具体位宽。
运行时确认构建配置
构建完成后,可以通过scipy.show_config()在运行时核对当前构建的配置细节,重点关注其中的'blas cython ilp64'条目,它直接反映 Cython 层的整数 ABI 设置。
Cython BLAS/LAPACK 整数 ABI
blas_int 与 blas_bint 类型
Cython 层的 BLAS/LAPACK API(scipy.linalg.cython_blas与scipy.linalg.cython_lapack)对所有整型参数统一使用blas_int类型。默认情况下,blas_int跟随use-ilp64设置:LP64 构建解析为 C 的int(32 位),ILP64 构建解析为int64_t(64 位)。这一映射关系可在 scipy/_build_utils/_wrappers_common.py 中看到'blas_int': 'CBLAS_INT'的定义。
部分 LAPACK 函数使用布尔变量(Fortran 的logical),对应地cython_lapack使用blas_bint类型:当blas_int解析为 Cint时,blas_bint解析为 Cython 的bint;当blas_int解析为int64_t时,blas_bint同样解析为int64_t。
-Dcython-blas-abi 的三个取值
blas_int的行为可通过-Dcython-blas-abi构建选项覆盖,它接受三个值:
auto(默认):跟随use-ilp64设置;lp64:即使use-ilp64=true也始终使用 32 位整数;ilp64:始终使用 64 位整数。
例如,想要构建 ILP64 的 BLAS/LAPACK、但把 Cython API 保持在 LP64(为了下游兼容)时:
$ spin build -S-Dblas=accelerate -S-Duse-ilp64=true -S-Dcython-blas-abi=lp64这对Accelerate最为实用,因为它对"同时使用 LP64 与 ILP64"支持良好。MKL也支持这种混用——其mkl-dynamic-lp64-*共享库中提供了带_64符号后缀的 ILP64 符号;但文档特别提醒:切勿混链不同的共享库,即避免同时链接mkl_rt.so、mkl-dynamic-lp64-*.so与mkl-dynamic-ilp64-*.so。
在引入 ILP64 的过渡期,这个"SciPy 内部用 ILP64、Cython API 保持 LP64"的配置很有价值,因为下游包可能尚未在其 Cython BLAS/LAPACK 调用中支持 64 位整数。
下游 Cython 包如何消费 blas_int
文档强调,消费cython_blas或cython_lapack接口的下游包,理想情况下应在所有调用点直接使用blas_int类型。然而有些包更愿意继续使用int,并手动在int与blas_int之间做映射——把这种映射收敛到一个内部包装函数中(调用 LAPACK 前把int输入转成blas_int,返回后再把blas_int输出转回int)比较方便。但文档明确指出代价:这样做会把数组尺寸限制在< INT_MAX,即使底层 LAPACK 本身启用了 ILP64 也无济于事。
官方为此提供了一个可运行的完整示例,即仓库中的ilp64_test_package测试包(位于 scipy/linalg/tests/_cython_examples/ilp64_test_package)。该包的 README 清晰演示了两种适配路径:
- 包装器方案:通过 Cython 包装层把
int强制转换为blas_int,其他模块只依赖这个包装层,无需改动。其.pxd声明(src/ilp64_test_package/_blas_lapack_wrappers.pxd)注释指出,这模拟了 scikit-learn 与 statsmodels 的既有模式——内部 API 使用int,需要适配到 ILP64 构建下 BLAS/LAPACK 期望的 64 位整数。效果是无论 SciPy 内部是 LP64 还是 ILP64,对外暴露的线性代数始终是 LP64(测试见 tests/test_wrappers.py)。 - 直接使用方案:不经过中间包装层,直接使用 SciPy 的
blas_int类型,这样下游功能不受INT_MAX对数组尺寸的限制(测试见 tests/test_direct.py)。
test_direct.py 中的test_dnrm2_large_vector是检验 ILP64 是否真正生效的典型用例:它构造了一个长度为2**31的向量(超过 32 位int上限)——当blas_int为 64 位时dnrm2计算结果正确为 1.0,而仍为 32 位时结果退化为 0.0,直观展示了 ILP64 在大规模计算中的价值。
工作中的计划:多选项自动择优
文档最后列出一项尚未开箱即用的计划能力:自动从多个可用的 BLAS 与 LAPACK 候选中,按用户给定的优先级顺序进行选择。目前这一功能仍在规划中,当前版本仍需用户在-Dblas=/-Dlapack=中显式指定单一实现(对应 meson.options 中blas/lapack均为 string 类型选项这一事实)。对于需要同时测试多套后端的用户,现阶段建议通过切换PKG_CONFIG_PATH或直接修改构建参数来实现。
小结:一条命令对应一类场景
| 场景 | 推荐命令 |
|---|---|
| 开发构建,使用纯 Netlib 库 | spin build -S-Dblas=blas -S-Dlapack=lapack |
| 打包 wheel,使用 Accelerate | python -m build -Csetup-args=-Dblas=accelerate |
| 打包 wheel,使用 MKL | python -m build -Csetup-args=-Dblas=mkl-dynamic-lp64-gomp |
| 仅用 BLIS(需另配 LAPACK) | -Dblas=blis -Dlapack=<lapack 实现> |
| 链接非标准路径独立库 | 手工编写.pc文件 + 设置PKG_CONFIG_PATH |
| 针对 g77 ABI 库强制包装 | -C-Duse-g77-abi=true |
| 启用 ILP64(Cython 层保持 LP64) | -Duse-ilp64=true -Dcython-blas-abi=lp64 |
| 运行时核对 ABI 配置 | scipy.show_config()查看'blas cython ilp64' |
本文全部命令行与参数均以当前仓库 meson.options 中的实际默认值和构建文档 doc/source/building/blas_lapack.rst 为准。配置环境前,请先确认目标 BLAS/LAPACK 已通过pkg-config或 CMake 正确安装,并以pkg-config --libs与--cflags验证.pc文件解析无误。
- 科学计算
- 数据科学
- 高性能计算
【免费下载链接】scipy
SciPy library main repository
相关推荐
NumPy 构建指南:BLAS 与 LAPACK 库的自动检测、手动选择与配置调优
NumPy 构建指南:BLAS 与 LAPACK 库的自动检测、手动选择与配置调优 本文是 NumPy 从源码构建系列指南中的核心章节,围绕 doc/sourc
科学计算数据分析SciPy 1.18.0 版本全解析:BLAS/LAPACK 多 ABI 构建、Whittaker-Henderson 平滑与大规模 Array API 支持
SciPy 1.18.0 版本全解析:BLAS/LAPACK 多 ABI 构建、Whittaker Henderson 平滑与大规模 Array API 支持
科学计算数据科学高性能计算SciPy 低层 LAPACK 接口完全指南:scipy.linalg.lapack 的函数查找、类型前缀与 ILP64 实践
SciPy 低层 LAPACK 接口完全指南:scipy.linalg.lapack 的函数查找、类型前缀与 ILP64 实践 导读 scipy.linalg.
科学计算数据科学高性能计算
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考