← Back to Blogs
HN Story

The Native Dependency Nightmare: BLAS, LAPACK, and OpenMP in Python

May 12, 2026

The Native Dependency Nightmare: BLAS, LAPACK, and OpenMP in Python

For the modern data scientist, installing a library like NumPy or scikit-learn feels like a trivial pip install. However, beneath the surface of these Python wheels lies a complex and often fragile web of native dependencies. The high-performance computing (HPC) foundations of the PyData stack—specifically BLAS, LAPACK, and OpenMP—represent one of the most significant technical hurdles in Python packaging today.

These libraries are not merely "dependencies" in the traditional sense; they are the engine rooms of scientific computing, written in a mix of C, C++, Fortran, and assembly to squeeze every ounce of performance from the hardware. Because they are typically not packaged on PyPI, the ecosystem has evolved into a fragmented landscape of "vendoring" and implicit dependencies that create systemic instability.

The Core Trio: BLAS, LAPACK, and OpenMP

To understand the packaging struggle, one must first understand the roles of these three components:

  • BLAS (Basic Linear Algebra Subprograms): The fundamental routines for vector and matrix operations.
  • LAPACK (Linear Algebra PACKage): A higher-level library built on top of BLAS for more complex linear algebra routines.
  • OpenMP (Open Multi-Processing): An API for shared-memory parallel programming, allowing code to run across multiple CPU cores.

While reference implementations exist (such as those from Netlib), the real-world performance gains come from optimized implementations like Intel MKL, OpenBLAS, Apple Accelerate, and AMD AOCL. These optimized versions often provide 10x to 100x performance improvements over reference code, making them indispensable for deep learning and scientific research.

The "Vendoring" Trap and the PyPI Dilemma

Because PyPI is designed primarily for Python code, it struggles with native libraries that have complex Application Binary Interfaces (ABIs). To ensure that users can actually run their code, many major projects have turned to vendoring—bundling the compiled native library directly inside the Python wheel.

This approach creates several critical issues:

1. The "One Version Rule" Violation

In a healthy environment, you want a single version of a library. However, because NumPy and SciPy both vendor OpenBLAS, a single environment can end up with multiple copies of the same library, often in different versions. For instance, NumPy may move to a 64-bit (ILP64) build while SciPy remains on a 32-bit build, leading to potential instability.

2. Threading Oversubscription and Deadlocks

When multiple libraries each bring their own threading model, chaos ensues. Mixing pthreads (used by some OpenBLAS builds) with OpenMP can lead to oversubscription, where the system attempts to spawn more threads than there are physical cores, tanking performance.

More dangerously, certain OpenMP implementations (like GCC's libgomp) are not "fork-safe." When combined with Python's multiprocessing module (which defaults to fork on POSIX), this frequently results in hard-to-debug deadlocks.

3. The Diamond Dependency Problem

When NumPy and SciPy both depend on OpenBLAS, they create a diamond dependency. If they vendor different versions, upgrading one without the other becomes a minefield of potential segfaults or numerical inaccuracies.

Comparison: PyPI vs. System Package Managers

The struggle of the Python community highlights a fundamental difference between language-specific package managers and system-level managers (like Debian's apt, Fedora's dnf, or Conda).

Feature PyPI (Wheels) System Managers (Conda/Debian)
Dependency Model Self-contained (Vendored) Shared/Virtual Dependencies
BLAS Selection Fixed at build time Runtime switching (e.g., FlexiBLAS)
ABI Management Implicit/Fragile Explicit via mutex metapackages
Parallelism Often conflicting Coordinated

Conda, for example, uses "mutex metapackages" to ensure that only one implementation of BLAS is installed at a time, allowing users to switch between MKL and OpenBLAS without breaking their environment.

The Path Forward

Solving the native dependency crisis requires moving away from the "small island" mentality of language-specific package managers. Potential solutions include:

  • Dedicated Native Wheels: Creating separate PyPI packages for OpenBLAS and OpenMP to eliminate duplication.
  • Demuxing Libraries: Utilizing wrapper libraries like FlexiBLAS or SciPy's cython_blas to provide a uniform API/ABI, allowing the underlying implementation to be swapped at runtime.
  • System Integration: Improving the ability of Python packages to express dependencies on libraries existing outside of PyPI, effectively bridging the gap between the Python ecosystem and the OS package manager.

Community Perspectives

The debate over this architecture reflects a broader frustration with how modern languages handle native code. One contributor noted that language-specific managers often ignore system libraries and ABIs during their design phase, only for the ecosystem to "blow up spectacularly in production" once it reaches a certain scale.

While some look toward the future—such as C++26's proposed linear algebra subset in the standard library—the immediate challenge remains: coordinating the complex interplay between Fortran-based linear algebra and the Python-centric world of PyPI.

References

HN Stories