Python Packaging's "Fast" Approach Creates Future Performance Bottlenecks

Original Title: #544: Wheel Next + Packaging PEPs

The Hidden Cost of "Fast" Python: Why Today's Packaging Solutions Create Tomorrow's Problems

The core thesis of this conversation is that the current approach to Python packaging, driven by a desire for immediate compatibility and ease of installation, inadvertently creates significant downstream performance limitations and complexity for users and developers alike. The non-obvious implication is that "solving" packaging problems with a lowest-common-denominator mindset, while seemingly efficient in the short term, actively prevents Python from leveraging modern hardware capabilities, leading to bloated binaries, slower execution, and a fractured user experience. This discussion is crucial for package maintainers, developers building performance-sensitive applications, and anyone invested in the future scalability and efficiency of the Python ecosystem. Those who engage with these insights will gain a strategic advantage by anticipating and contributing to solutions that unlock significant performance gains and simplify complex installation processes.

The Invisible Ceiling: Why Today's Wheels Limit Tomorrow's Performance

The current Python packaging system, particularly for packages with compiled code, operates under a fundamental constraint: it prioritizes broad compatibility over specialized performance. This "lowest common denominator" approach means that wheels--the pre-compiled binary packages that pip and other installers distribute--are often built using CPU instruction sets that are over a decade old. The consequence? A massive performance gap between what modern hardware is capable of and what Python packages can actually deliver.

Jonathan Dekhtiar from NVIDIA highlights this stark reality: "If you build a wheel for that, you can only use CPU features, performance CPU features that go back to about 2009. Any new hardware features that were introduced after 2009, things like, you know, SSE4, AVX2, you know, later versions of that, you just cannot use because the installers don't know that you put that in the wheel, and hence they will also install it on computers that don't have those instructions, right? And then you just get like very ugly crashes." This forces maintainers to ship binaries that are significantly less performant than they could be, often resulting in "fat binaries" that are gigabytes in size, as Charlie Marsh of Astral notes.

The problem isn't just about CPUs. GPU support, for instance, requires specific CUDA versions and driver compatibility. Currently, installers lack the intelligence to select the correct build for a user's specific hardware. This forces users into complex, manual configurations, often involving special index URLs, turning what should be a simple pip install into a "puzzle book" of instructions.

"The difference between the 2009 hardware features and, you know, the 2019 or 2023 one could be a factor of 10x, 20x in performance, depending on what you're doing."

-- Ralf Gommers

This limitation directly impacts the performance of critical scientific computing and machine learning libraries. Ralf Gommers, from Quansight and the NumPy/SciPy teams, explains how NumPy currently tackles this: a complex internal dispatch system that detects the CPU at runtime and selects the appropriate code path. While ingenious, this approach is difficult to maintain and scales poorly to other packages. SciPy, for example, has optimized code for modern instruction sets like AVX2 and ARM Neon, but these optimized versions are not shipped because there's no mechanism to distribute them selectively. The consequence is that many packages are leaving substantial performance gains on the table, not due to a lack of optimization, but due to a lack of intelligent distribution.

The "Fat Binary" Dilemma: Why More Isn't Always Better

The current approach to distributing optimized code often leads to "fat binaries"--large, monolithic packages that contain code for a wide array of hardware. While this ensures broad compatibility, it comes at a significant cost. As Charlie Marsh points out, a PyTorch wheel can be around 900 megabytes, partly because it bundles support for multiple CUDA architectures. This bloats download sizes, increases bandwidth consumption for both users and infrastructure providers like PyPI, and slows down installation times.

The proposed "Wheel Variants" PEPs aim to solve this by allowing packages to declare their hardware requirements. Installers like UV could then automatically select the most appropriate, slimmed-down variant for the user's specific machine. This means a user with a modern GPU and compatible drivers would receive a PyTorch wheel optimized for their specific CUDA version, potentially reducing its size to 200-50 megabytes. This shift from a single, massive binary to multiple, specialized variants offers a compelling advantage: efficiency.

"If we have variants, we can just slim it down to one CUDA architecture per wheel, you know, so you can go down to like, you know, 200 megabytes or so, to 50 maybe. But it's way better for, you know, both for index servers, it's better for users. It's, it's going to be pretty slow too."

-- Charlie Marsh

The immediate downside, acknowledged by the team, is the increased burden on CI servers that must build these multiple variants. However, this is framed as a one-time cost for a scalable, long-term benefit. The build is done once, but the optimized installation is repeated millions of times, leading to substantial savings in bandwidth and installation speed. This highlights a core principle of systems thinking: immediate discomfort (CI build complexity) can lead to lasting advantage (efficient distribution and faster installs).

The Long Tail of Adoption: When Progress Meets Inertia

The journey from proposing new standards (PEPs) to widespread adoption is notoriously long and complex. The "Wheel Next" initiative, encompassing several PEPs including PEP 817 (Wheel Variants) and PEP 825 (Wheel Variants Package Format), is a prime example. While prototypes exist and the design is iterating rapidly, the full ecosystem--from build backends and package index servers like PyPI to installers like pip and uv--must adapt.

Jonathan Dekhtiar notes that even after PEPs are accepted, there's a "very long tail of implementation" as tools and infrastructure are updated. This process can take years. Furthermore, the adoption rate of new tools like uv versus established ones like pip plays a significant role. While uv users tend to adopt newer features more quickly, a substantial portion of the Python community still uses older versions of pip, creating a drag on widespread adoption of new packaging standards.

The strategy to mitigate this involves focusing on key influential packages. The expectation is that if major libraries like PyTorch, JAX, and their critical dependencies adopt wheel variants, it will create a powerful network effect. This selective adoption, targeting packages that experience the most pain from current limitations, is a pragmatic approach. Ralf Gommers suggests that once PyTorch and similar libraries embrace variants, "half of its dependencies will probably activate variant mode. And then the people that build on top of PyTorch or people who build on top of JAX. So just that you end up with at least 50 packages in a matter of a few months." This demonstrates how targeted intervention in a complex system can cascade positive change.

Key Action Items

  • For Package Maintainers:

    • Immediate Action: Familiarize yourself with the Wheel Variants PEPs (PEP 817, PEP 825) and related discussions on discuss.python.org.
    • Medium-Term Investment (6-12 months): Begin prototyping the generation of wheel variants for your package, especially if it contains compiled code or has specific hardware dependencies (e.g., SIMD instructions, CUDA).
    • Long-Term Investment (12-18 months): Plan for the infrastructure changes needed to publish multiple wheel variants to PyPI or other registries.
  • For Users of Performance-Sensitive Libraries (e.g., ML/Data Science):

    • Immediate Action: Try using the experimental, variant-enabled builds of tools like uv (e.g., by pointing to a specific URL) to install packages like PyTorch. This provides valuable feedback to the developers.
    • Medium-Term Action: Advocate for the adoption of wheel variants within the projects you use by reporting issues and engaging in their community forums.
  • For Tool Authors (Installers, Build Backends, Registries):

    • Immediate Action: Actively participate in the PEP review process on discuss.python.org.
    • Medium-Term Investment (6-12 months): Begin implementing support for wheel variants in your tools, starting with installers like uv and pip, and build systems.
    • Long-Term Investment (12-18 months): Ensure your tools can correctly identify, select, and install wheel variants, and that registries can host and serve them efficiently.
  • For All Stakeholders:

    • Immediate Action: Contribute to the py-packaging-native-guide website if you have insights into packaging problems, especially those related to hardware acceleration.
    • Medium-Term Investment: Engage with the Wheel Next working group and related community discussions to help refine the design and implementation.
  • Embrace Discomfort for Future Advantage:

    • Immediate Action: Be prepared for initial complexity and potential friction when experimenting with variant-enabled builds. This discomfort now is a necessary precursor to the future simplicity and performance gains.
    • Long-Term Payoff (18+ months): The adoption of wheel variants will lead to significantly faster installations, smaller download sizes, and unlock previously inaccessible performance optimizations, creating a more robust and efficient Python ecosystem.

---
Handpicked links, AI-assisted summaries. Human judgment, machine efficiency.
This content is a personally curated review and synopsis derived from the original podcast episode.