swadhinbiswas

[email protected]
2026-07-16 16:49:03 +0600 committed NaN years ago
f43d79f
commit f43d79f
1 file changed
+35 -8
Showing 1 changed file+35-8
▼BENCHMARKS.md
+35-8
157
157
body echoed verbatim; handlers that transform the body must keep using a Python
158
158
handler.
159
159
160
-> **Known issue — non-native deadlock at high concurrency (see ADR-049):** the
161
-> *Python* dispatch path (`spawn_blocking` + `Python::attach`) hard-stalls at
162
-> ~100 concurrent connections for **every** non-native route, including the
163
-> simplest handler (≈16–20 req/s, all connections aborted). The native fast path
164
-> is immune because it never acquires the GIL or enters the blocking pool. This
165
-> is a pre-existing server-level GIL/blocking-pool deadlock, **not** introduced
166
-> by the native optimization; it must be fixed before non-native routes are
167
-> production-viable at scale (recommended fix: a dedicated GIL thread-pool).
160
+> **Non-native dispatch deadlock — RESOLVED (ADR-049).** The dedicated GIL
161
+> thread-pool (`gil_pool.rs`) replaced per-request `spawn_blocking` +
162
+> `Python::attach`; the Python path no longer stalls at high concurrency (verified
163
+> at `-c 200`, 100% success). On CPython the Python path is still GIL-serialized
164
+> (~100–120k req/s ceiling on this hardware) — that is the GIL, not a deadlock.
165
+> The native fast path remains the high-throughput option.
166
+
167
+> **Measuring the native fast path correctly:** the 410k–724k numbers require a
168
+> route registered with **both** `native=True` **and** a `Schema`. `native=True`
169
+> *without* a schema silently falls back to the Python GIL path (see the caveat
170
+> below), which is why a naively-tagged route benchmarks at ~120k, not ~700k. The
171
+> benchmark below reproduces the methodology on current hardware.
172
+
173
+### Current-hardware re-run (2026-07-16, release build)
174
+
175
+Same methodology as the fixture (`oha -z 10s -c 100`, single-process, release
176
+`maturin develop --release`) but on the dev box (different CPU bin). This
177
+substantiates the fast-path claim here and isolates the three variables that
178
+move the number: **build profile**, **concurrency**, and **whether the route
179
+actually takes the Rust fast path**.
180
+
181
+| Route | Registration | Mode | req/sec @ -c 100 |
182
+|---|---|---|---|
183
+| `/validate_native` | `native=True, schema=UserSchema` | **Rust fast path** | **430,367** |
184
+| `/python_route` | plain handler | Python GIL path | 123,170 |
185
+| `/native` (no schema) | `native=True` only | Python GIL path (fallback) | ~120,000 |
186
+
187
+- On a **dev** build (`maturin develop`, no `--release`) the Python path drops to
188
+ ~64k req/s — optimization level, not a deadlock.
189
+- The Python path is GIL-serialized; ~100–120k req/s is its realistic ceiling on
190
+ this hardware. The 430k native number is ~3.5× the Python path and matches the
191
+ lower end of the fixture's 410k–724k range (the fixture CPU is a faster bin).
192
+- **Takeaway:** to actually beat Robyn-class raw throughput, opt routes into
193
+ `native=True` + a `Schema`. Plain Python handlers are correct and stable but
194
+ GIL-bound.
168
195
169
196
---
170
197
171
198