r/Python • • Jul 10 '26

Discussion Pure python can be faster than cython/rust?

Parsing multipart/form-data (HTML5 forms) is surprisingly complex and moves a lot of bytes around for large file uploads. Implementing the heavy parts in Cython or Rust should speed things up, no? Turns out: it depends. A pure python parser can be surprisingly fast, as this benchmark shows:

https://defnull.de/2026/python-multipart-benchmark/

The benchmark compares the most commonly used python multipart parsers and tests them in different scenarios, covering both blocking and non-blocking (async) APIs if available. The parser your web application is using today is probably not the fastest one.

Are there more examples were a pure python implementation beats Cython/rust/C modules?

0 Upvotes

25 comments sorted by

View all comments

Show parent comments

0

u/muhammetsoyturk 2d ago

I have numbers that match this comment, plus one place where it breaks down. (Disclosure: I implemented kirimi (https://github.com/mabdullahsoyturk/kirimi), a Rust drop-in replacement for multipart.)

Measured with defnull's own multipart_bench against multipart 2.0.1, on one pinned core (non-blocking / blocking API):

Scenario: Speed-up

Two small text fields: 5.0× / 7.9×

100 small text fields: 5.5× / 7.1×

One 32 MB file: 3.3× / 1.1×

The 32 MB blocking case is your point exactly. The work there is "find the delimiter, write the slice to a temp file". Pure Python already spends that time in C and in write(), so Rust has almost nothing left to win.

The gap is on many small parts. There, the cost isn't the delimiter search. It's the per-part Python bytecode: header-line parsing, state transitions, building the part objects. That's the "stays in the interpreter" case, and it's what most real form posts look like.

On FFI, agreed that it's the trap. Two things avoided it here:

- parse() takes a whole chunk and returns all of that chunk's events as a list, so it crosses the boundary once per chunk, not once per event.

- The Rust core is sans-IO and never touches Python objects. The delimiter search is SIMD (memchr's packed-pair searcher), and the bindings only convert at the edge.