logoalt Hacker News

How uv got so fast

1009 pointsby zdwyesterday at 5:13 PM338 commentsview on HN

Comments

orliesaurusyesterday at 11:40 PM

The most surprising part of uv's success to me isn't Rust at all, it's how much speed we "unlocked" just by finally treating Python packaging as a well-specified systems problem instead of a pile of historical accidents. If uv had been written in Go or even highly optimized CPython, but with the same design decisions (PEP 517/518/621/658 focus, HTTP range tricks, aggressive wheel-first strategy, ignoring obviously defensive upper bounds, etc.), I strongly suspect we'd be debating a 1.3× vs 1.5× speedup instead of a 10× headline — but the conversation here keeps collapsing back to "Rust rewrite good/bad." That feels like cargo-culting the toolchain instead of asking the uncomfortable question: why did it take a greenfield project to give Python the package manager behavior people clearly wanted for the last decade?

show 14 replies
woodruffwyesterday at 8:24 PM

I think this post does a really good job of covering how multi-pronged performance is: it certainly doesn't hurt uv to be written in Rust, but it benefits immensely from a decade of thoughtful standardization efforts in Python that lifted the ecosystem away from needing `setup.py` on the hot path for most packages.

show 3 replies
epageyesterday at 8:49 PM

> uv is fast because of what it doesn’t do, not because of what language it’s written in. The standards work of PEP 518, 517, 621, and 658 made fast package management possible. Dropping eggs, pip.conf, and permissive parsing made it achievable. Rust makes it a bit faster still.

Isn't assigning out what all made things fast presumptive without benchmarks? Yes, I imagine a lot is gained by the work of those PEPs. I'm more questioning how much weight is put on dropping of compatibility compared to the other items. There is also no coverage for decisions influenced by language choice which likely influences "Optimizations that don’t need Rust".

This also doesn't cover subtle things. Unsure if rkyv is being used to reduce the number of times that TOML is parsed but TOML parse times do show up in benchmarks in Cargo and Cargo/uv's TOML parser is much faster than Python's (note: Cargo team member, `toml` maintainer). I wish the TOML comparison page was still up and showed actual numbers to be able to point to.

show 2 replies
pechenyyesterday at 8:50 PM

The content is nice and insightful! But God I wish people stopped using LLMs to 'improve' their prose... Ironically, some day we might employ LLMs to re-humanize texts that had been already massacred.

show 9 replies
ethinyesterday at 8:32 PM

> Zero-copy deserialization. uv uses rkyv to deserialize cached data without copying it. The data format is the in-memory format. This is a Rust-specific technique.

This (zero-copy deserialization) is not a rust-specific technique, so I'm not entirely sure why the author describes it as one. Any good low level language (C/C++ included) can do this from my experience.

show 4 replies
sghaztoday at 1:33 PM

Liked the focus on standards and ecosystem decisions rather than just “it’s fast because Rust.”

One small timeline nit: the article mentions PEP 517 as being from 2017, but the PEP itself was created in 2015. From the PEP header:

Created: 30-Sep-2015 [1]

It did see important revisions and wider adoption around 2017, so I assume that’s what was meant.

[1] https://peps.python.org/pep-0517/

ofekyesterday at 9:47 PM

> pip could implement parallel downloads, global caching, and metadata-only resolution tomorrow. It doesn’t, largely because backwards compatibility with fifteen years of edge cases takes precedence.

pip is simply difficult to maintain. Backward compatibility concerns surely contribute to that but also there are other factors, like an older project having to satisfy the needs of modern times.

For example, my employer (Datadog) allowed me and two other engineers to improve various aspects of Python packaging for nearly an entire quarter. One of the items was to satisfy a few long-standing pip feature requests. I discovered that the cross-platform resolution feature I considered most important is basically incompatible [1] with the current code base. Maintainers would have to decide which path they prefer.

[1]: https://github.com/pypa/pip/issues/13111

show 1 reply
punnerudyesterday at 10:53 PM

My favorite speed up trick: “ HTTP range requests for metadata. Wheel files are zip archives, and zip archives put their file listing at the end. uv tries PEP 658 metadata first, falls back to HTTP range requests for the zip central directory, then full wheel download, then building from source. Each step is slower and riskier. The design makes the fast path cover 99% of cases. None of this requires Rust.”

show 1 reply
Revisional_Sintoday at 8:03 AM

> Ignoring requires-python upper bounds. When a package says it requires python<4.0, uv ignores the upper bound and only checks the lower. This reduces resolver backtracking dramatically since upper bounds are almost always wrong. Packages declare python<4.0 because they haven’t tested on Python 4, not because they’ll actually break. The constraint is defensive, not predictive.

Erm, isn't this a bit bad?

show 2 replies
blintzyesterday at 8:24 PM

> PEP 658 went live on PyPI in May 2023. uv launched in February 2024. The timing isn’t coincidental. uv could be fast because the ecosystem finally had the infrastructure to support it. A tool like uv couldn’t have shipped in 2020. The standards weren’t there yet.

How/why did the package maintainers start using all these improvements? Some of them sound like a bunch of work, and getting a package ecosystem to move is hard. Was there motivation to speed up installs across the ecosystem? If setup.py was working okay for folks, what incentivized them to start using pyproject.toml?

show 3 replies
w10-1yesterday at 10:21 PM

I like the implication that we can have an alternative to uv speed-wise, but I think reliability and understandability are more important in this context (so this comment is a bit off-topic).

What I want from a package manager is that it just works.

That's what I mostly like about uv.

Many of the changes that made speed possible were to reduce the complexity and thus the likelihood of things not working.

What I don't like about uv (or pip or many other package managers), is that the programmer isn't given a clear mental model of what's happening and thus how to fix the inevitable problems. Better (pubhub) error messages are good, but it's rare that they can provide specific fixes. So even if you get 99% speed, you end up with 1% perplexity and diagnostic black boxes.

To me the time that matters most is time to fix problems that arise.

show 1 reply
andy99yesterday at 9:05 PM

I remain baffled about these posts getting excited about uv’s speed. I’d like to see a real poll but I personally can’t imagine people listing speed as one of the their top ten concerns about python package managers. What are the common use cases where the delay due to package installation is at all material?

Edit to add: I use python daily

show 21 replies
didibusyesterday at 9:30 PM

There's an interesting psychology at play here as well, if you are a programmer that chooses a "fast language" it's indicative of your priorities already, it's often not much the language, but that the programmer has decided to optimize for performance from the get go.

yjftsjthsd-hyesterday at 8:14 PM

> No bytecode compilation by default. pip compiles .py files to .pyc during installation. uv skips this step, shaving time off every install. You can opt in if you want it.

Are we losing out on performance of the actual installed thing, then? (I'm not 100% clear on .pyc files TBH; I'm guessing they speed up start time?)

show 6 replies
bastawhizyesterday at 9:36 PM

> When a package says it requires python<4.0, uv ignores the upper bound and only checks the lower. This reduces resolver backtracking dramatically since upper bounds are almost always wrong. Packages declare python<4.0 because they haven’t tested on Python 4, not because they’ll actually break. The constraint is defensive, not predictive.

This is kind of fascinating. I've never considered runtime upper bound requirements. I can think of compelling reasons for lower bounds (dropping version support) or exact runtime version requirements (each version works for exact, specific CPython versions). But now that I think about it, it seems like upper bounds solve a hypothetical problem that you'd never run into in practice.

If PSF announced v4 and declared a set of specific changes, I think this would be reasonable. In the 2/3 era it was definitely reasonable (even necessary). Today though, it doesn't actually save you any trouble.

show 2 replies
esttoday at 2:10 AM

> Virtual environments required

This bothers me more than once when building a base docker image. Why would I want a venv inside a docker with root?

show 2 replies
vjay15today at 10:54 AM

Amazing that how much python's pip was so bottlenecked, it was basic design problem damn

robertclausyesterday at 10:20 PM

At Plotly we did a decent amount of benchmarking to see how much the different defaults `uv` uses lead to its performance. This was necessary so we could advise our enterprise customers on the transition. We found you lost almost all of the speed gains if you configured uv behave as much like pip as you could. A trivial example is the precompile flag, which can easily be 50% of pips install time for a typical data science venv.

https://plotly.com/blog/uv-python-package-manager-quirks/

show 1 reply
annexrichmondtoday at 5:37 AM

> This reduces resolver backtracking dramatically since upper bounds are almost always wrong.

I am surprised by this because Python minor versions break backwards compatibility all the time. Our company for example is doing a painful upgrade from py39 to py311

show 1 reply
simonwyesterday at 11:06 PM

This post is excellent. I really like reading deep dives like this that take a complex system like uv and highlight the unique design decisions that make it work so well.

I also appreciate how much credit this gives the many previous years of Python standards processes that enabled it.

Update: I blogged more about it here, including Python recreations of the HTTP range header trick it uses and the version comparison via u64 integers: https://simonwillison.net/2025/Dec/26/how-uv-got-so-fast/

ggmyesterday at 10:08 PM

Some of these speed ups looked viable to backport into pip including parallel download, delayed .pyc, ignore egg, version checks.

Not that I'd bother since uv does venv so well. But, "it's not all rust runtime speed" implies pip could be faster too.

rao-vtoday at 4:41 AM

I have to say it's just lovely seeing such a nicely crafted and written technical essay. It's so obvious that this is crafted by hand, and reading it just reemphasises how much we've lost because technical bloggers are too ready to hand the keys over to LLMs.

show 1 reply
eviksyesterday at 9:54 PM

> Every code path you don’t have is a code path you don’t wait for.

No, every code path you don't execute is that. Like

> No .egg support.

How does that explain anything if the egg format is obsolete and not used?

Similar with spec strictness fallback logic - it's only slow if the packages you're installing are malformed, otherwise the logic will not run and not slow you down.

And in general, instead of a list of irrelevant and potentially relevant things would be great to understand some actual time savings per item (at least those that deliver the most speedup)!

But otherwise great and seemingly comprehensive list!

show 1 reply
dangoodmanUTtoday at 12:37 AM

> Zero-copy deserialization

Just a nit on this section: zero-copy deserialization is not Rust specific (see flatbuffers). rkyv as a crate for doing so in Rust is though

VerifiedReportsyesterday at 9:01 PM

So... will uv make Python a viable cross-platform utility solution?

I was going to learn Python for just that (file-conversion utilities and the like), but everybody was so down on the messy ecosystem that I never bothered.

show 3 replies
zahlmanyesterday at 9:27 PM

I've talked about this many times on HN this year but got beaten to the punch on blogging it seems. Curses.

... Okay, after a brief look, there's still lots of room for me to comment. In particular:

> pip’s slowness isn’t a failure of implementation. For years, Python packaging required executing code to find out what a package needed.

This is largely refuted by the fact that pip is still slow, even when installing from wheels (and getting PEP 600 metadata for them). Pip is actually still slow even when doing nothing. (And when you create a venv and allow pip to be bootstrapped in it, that bootstrap process takes in the high 90s percent of the total time used.)

didipyesterday at 11:17 PM

If UV team has a spare time, they should rewrite Python in Rust without any of the legacy baggage.

ec109685yesterday at 8:53 PM

The article info is great, but why do people put up with LLM ticks and slop in their writing? These sentences add no value and treats the reader as stupid.

> This is concurrency, not language magic.

> This is filesystem ops, not language-dependent.

Duh, you literally told me that the previous sentence and 50 million other times.

show 1 reply
hk1337yesterday at 11:17 PM

It’s fast because it sucks the life force from bad developers to make them into something good.

Jokes aside…

I really like uv but also really like mise and I cannot seem to get them to work well together.

show 1 reply
PrettyPastrytoday at 3:40 AM

I wish this were enough to get the flake8 devs to accept pyproject support PRs.

show 1 reply
shevy-javatoday at 3:08 AM

Soon uv will deliver results without you even thinking about them beforehand!

nurettinyesterday at 8:28 PM

> When a package says it requires python<4.0, uv ignores the upper bound and only checks the lower.

I will bring popcorn on python 4 release date.

show 3 replies
BiteCode_devyesterday at 11:10 PM

Other design decisions that made uv fast:

- uncompressing packages while they are still being downloaded, in memory, so that you only have to write to disk once

- design of its own locking format for speed

But yes, rust is actually making it faster because:

- real threads, no need for multi-processing

- no python VM startup overhead

- the dep resolution algo is exactly the type of workload that is faster in a compiled language

Source, this interview with Charlie Marsh: https://www.bitecode.dev/p/charlie-marsh-on-astral-uv-and-th...

The guy has a lot of interesting things to say.

show 2 replies
pwdisswordfishyyesterday at 9:03 PM

> Some of uv’s speed comes from Rust. But not as much as you’d think. Several key optimizations could be implemented in pip today: […] Python-free resolution

Umm…

TrayKnotstoday at 10:17 AM

I usually don't see the importance of speed in one-time costs... But hey, same discussion with npm, yarn, pnpm...

looneysquashyesterday at 8:59 PM

I don't have any real disagreement with any of the details the author said.

But still, I'm skeptical.

If it is doable, the best way to prove it is to actually do it.

If no one implements it, was it ever really doable?

Even if there is no technical reason, perhaps there is a social one?

show 2 replies
dmarwicketoday at 4:19 AM

wait, zero-copy deserialization isn't rust-specific. you can mmap structs in C. done it before, works fine

show 1 reply
IshKebabyesterday at 10:05 PM

Mmm I don't buy it. Not many projects use setup.py now anyway and pip is still super slow.

> Plenty of tools are written in Rust without being notably fast.

This also hasn't been my experience. Most tools written in Rust are notably fast.

show 2 replies
agumonkeyyesterday at 8:37 PM

very nice article, always good to get a review of what a "simple" looking tool does behind the scense

about rust though

some say a nicer language helps finding the right architecture (heard that about cpp veteran dropping it for ocaml, any attempted idea would take weeks in cpp, was a few days in ocaml, they could explore more)

also the parallelism might be a benefit the language orientation

enough semi fanboyism

zzzeekyesterday at 11:19 PM

> pip could implement parallel downloads, global caching, and metadata-only resolution tomorrow. It doesn’t, largely because backwards compatibility with fifteen years of edge cases takes precedence. But it means pip will always be slower than a tool that starts fresh with modern assumptions.

what does backwards compatibility have to do with parallel downloads? or global caching? The metadata-only resolution is the only backwards compatible issue in there and pip can run without a setup.py file being present if pyproject.toml is there.

Short answer is most, or at least a whole lot, of the improvements in uv could be integrated into pip as well (especially parallelizing downloads). But they're not, because there is uv instead, which is also maintained by a for-profit startup. so pip is the loser

aswegs8yesterday at 8:48 PM

uv seems to be a pet peeve of HN. I always thought pipenv was good but yeah, seems like I was being ignorant

show 4 replies
hallvardyesterday at 8:56 PM

Great post, but the blatant chatgpt-esque feel hits hard… Don’t get me wrong, I love astral! and the content, but…

show 1 reply
pjjpotoday at 6:24 AM

> npm’s package.json is declarative

lol

pkaodevyesterday at 10:34 PM

AI slop

man4today at 5:21 AM

[dead]

rvzyesterday at 10:42 PM

TLDR: Because Rust.

This entire AI generated article with lots of text just to just say the obvious.

show 1 reply
efilifeyesterday at 9:59 PM

this shit is ChatGPT-written and I'm really tired of it. If I wanted to read chatgpt I would have asked it myself. Half of the article are nonsensical repeated buzzwords thrown in for absolutely no reason

skywhopperyesterday at 8:57 PM

This is great to read because it validates my impression that Python packaging has always been a tremendous overengineered mess. Glad to see someone finally realized you just need a simple standard metadata file per package.

show 1 reply
almostheretoday at 8:32 AM

Our next trick, getting people to stop writing code (so we can stop writing python)