Open-source project
Kludex/zuvloop avatar
Kludex/zuvloop

zuvloop: A Zig-Powered asyncio Event Loop That Promises Speed Without the Rewrite

A libuv event loop for asyncio, written in Zig.

99 stars8 forksPythonMIT

At a glance

What is it?
zuvloop replaces the default asyncio event loop with a libuv-based implementation written in Zig, claiming large speedups in scheduling and I/O while keeping the standard asyncio API. The project targets Python 3.14+ and is early-stage, with version 0.0.12 as the latest release.
Who is it for?
Adopt zuvloop if you run Python 3.14+ on a supported platform, rely heavily on asyncio scheduling or socket I/O, and want a drop-in replacement that preserves your existing Task and protocol code. Do not adopt it if you need Python versions below 3.14, require a stable API beyond version 0.0.x, or cannot tolerate potential regressions in edge cases that the compatibility gates may not cover.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • Kludex/zuvloop99 ★ · +58
Star history
Repository2026-08-272026-11-13
Kludex/zuvloop4199

58 Stars in the last 78 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What zuvloop solves and who it targets

zuvloop addresses a specific pain: the default asyncio event loop in CPython is pure Python for scheduling and timer management, which becomes a bottleneck for applications that create many tasks or handle high connection counts. The README claims over 25x faster than asyncio at thread-safe scheduling, and the benchmark table shows `call_soon_threadsafe` at 11.9M/s versus 0.44M/s for stock asyncio. The project is aimed at developers running Python 3.14 or newer who want to accelerate their asyncio applications without changing their code. It is a drop-in replacement: you keep the same Task objects, protocols, and APIs. The target audience is not beginners or those on older Python versions; it is teams running modern CPython who are willing to experiment with a young project that has not reached a 1.0 release.

How it works: libuv under the hood with Zig glue

The core mechanism is replacing the asyncio event loop's internal scheduling and I/O with libuv, the same C library that powers Node.js. The Zig code provides the binding layer, handling the translation between asyncio's callback model and libuv's watcher model. The architecture docs, referenced in the README, describe several specific techniques: argument storage inside handles to avoid allocating a tuple per callback, a native timer heap behind a single `uv_timer_t`, per-turn vectored write batching, zero-copy reads, and a fast path for `getaddrinfo` when the host is a numeric literal. These are concrete design choices that explain the benchmark deltas. For instance, the timer benchmarks show a dramatic improvement: completed timer rounds at 2.93M/s versus 71.1k/s for asyncio, which suggests the native heap avoids the per-timer Python object overhead. The event loop still schedules real `asyncio.Task` objects, so Python-level introspection tools like `python -m asyncio ps` work unchanged. This is a hybrid approach: the loop machinery is native, but the task and protocol objects remain Python.

Installation and the one-line migration

Installation is straightforward: `pip install zuvloop`. The project ships prebuilt wheels for Linux x86-64/AArch64, macOS x86-64/arm64, and Windows AMD64/ARM64. Source distributions pin a Zig 0.16 toolchain in an isolated build environment, so you do not need Zig installed unless you are doing native development. Using the loop is a one-line change. The README shows two patterns. The first uses `zuvloop.run(main())`, which replaces `asyncio.run()`. The second keeps `asyncio.run()` but passes the loop factory: `asyncio.run(main(), loop_factory=zuvloop.new_event_loop)`. That second pattern is the safer migration path because it preserves the standard entry point. The example code is ordinary asyncio: `asyncio.open_connection`, `writer.write`, `await reader.read`. No special APIs are introduced. This is the core promise: your code stays exactly as it is, and only the loop underneath changes.

Observability: OpenTelemetry spans and gauges without vendor lock-in

A notable feature is built-in OpenTelemetry instrumentation. The runtime depends only on `opentelemetry-api` and `typing-extensions`, not a full SDK. This means you can configure any OpenTelemetry-compatible backend, and the README gives a Logfire example. The instrumentation emits `zuvloop.slow_callback` spans with real timestamps from `uv_hrtime()` and attaches the awaiting call graph via `asyncio.format_call_graph()`. There are also `zuvloop.unhandled_exception` spans, counters, a callback-duration histogram, and loop gauges such as `loop_count`, `events`, `idle_time_ns`, `ready`, `timers`, and `watchers`. The gauges are sampled at a configurable interval (`loop.metrics_interval`, default 10 seconds), and they are only published once a real provider is installed. Until then, the instruments are no-ops and slow-callback timing is off. This design keeps the overhead at zero until you opt in, which is a sensible trade-off. The observability story is more integrated than what you get with stock asyncio, where you would need separate tooling.

Compatibility gates: how the project guards against regressions

The README describes three aggregate gates that every pull request must pass. The first covers the in-repository suite on Linux and macOS, the portable suite on Windows, musl runtime tests, cross-compilation, ReleaseSafe builds, and documentation. The second runs CPython conformance and pinned upstream suites from aiohttp, uvicorn, AnyIO, websockets, aioquic, Tornado, and HTTPX2, with zuvloop swapped in. The third runs a native sanitizer build and a 500-cycle resource-ownership soak. Additionally, the compatibility run tests CPython 3.14.0, the newest 3.14 patch, and standard and free-threaded 3.15 builds, then exercises gRPC AsyncIO, asyncpg, Psycopg, and redis-py against real local services. This is a substantial testing rig for a project at version 0.0.12. The exact commands and immutable commits are in `.github/workflows/compatibility.yml`, so you can reproduce the tests yourself. The inclusion of free-threaded builds is forward-looking, since Python 3.13 introduced experimental free-threading and 3.15 is expected to make it more mainstream.

Performance claims and the benchmark caveats

The README presents a benchmark table measured on an M3 Max, macOS 26, CPython 3.14, and libuv 1.51.0. Each result is the median of seven interleaved in-process runs or five interleaved HTTP runs. The headline numbers are impressive: `call_soon` at 8.81M/s versus 2.36M/s for asyncio, and `call_soon_threadsafe` at 11.9M/s versus 0.44M/s. But there are caveats. The timer benchmarks are described as measuring different work: schedule + cancel isolates heap bookkeeping, completed timer rounds chain one zero-delay timer per loop turn, and prebuilt due timer batch measures heap draining with allocation outside the timed section. This means you cannot compare those rows directly to each other. Also, the aiohttp client benchmark shows zuvloop at 14.8k req/s versus uvloop's 15.0k req/s, so it does not win everywhere. The README claims it matches or beats uvloop on 13 of 14 benchmarks, but that one loss is worth noting. The bulk stream and uvicorn numbers are strong, but they are from a single hardware configuration. You should reproduce these on your own hardware before making decisions.

Limitations and cases where zuvloop is the wrong tool

The most obvious limitation is the Python version requirement: 3.14+ only. If you are on 3.12 or 3.13, which are still common in production, zuvloop is not available. The project is also at version 0.0.12, which signals instability; API changes are possible before a 1.0 release. The README mentions compatibility with standard and free-threaded CPython builds, but free-threading is still experimental in 3.14, so that path carries its own risks. Another limitation is the lack of support for custom event loop policies that some frameworks expect. The README shows the `loop_factory` approach, but if your application or a dependency calls `asyncio.set_event_loop_policy()` with a custom policy, that may not interact cleanly with zuvloop. The project does not document a policy class, only a factory. For applications that rely on the default loop's exact timing behavior for debugging or deterministic tests, the native timer heap could change behavior. The README does not discuss compatibility with `asyncio` debug mode or `PYTHONASYNCIODEBUG`, which some teams use. Finally, the benchmark table does not include memory usage; the native handles might consume more memory per callback, though the README claims argument storage inside handles reduces allocations.

Alternatives: uvloop and the standard loop

The main alternative is uvloop, which is also a drop-in asyncio event loop but written in Cython and based on libuv as well. The README positions zuvloop as faster than uvloop on most benchmarks, with the aiohttp client being the exception. The key difference is language: uvloop is Cython, zuvloop is Zig. This matters for maintainability and build tooling. uvloop is more mature, with a longer history and wider adoption, but it may not support Python 3.15 or free-threaded builds as quickly. zuvloop claims support for those already. Another difference is observability: uvloop does not have built-in OpenTelemetry instrumentation, so you would need to add it separately. The standard asyncio loop is the baseline; it is pure Python, so it is slower but has zero extra dependencies and is guaranteed to work with every Python version. If you cannot use a native loop, the standard loop is the safe choice. For teams that want speed without changing code, both zuvloop and uvloop are viable, but the choice may come down to Python version support and the willingness to trust a Zig codebase.

Maintenance and upgrade cost

The project is actively developed, with three releases on the same day (v0.0.10, v0.0.11, v0.0.12) according to the timestamps. The release notes mention Python 3.15 support, faster task scheduling, and free-threaded event loop scaling. This suggests a fast iteration pace, which is good for fixing bugs but also means you may need to track releases closely. The README does not specify a deprecation policy or a migration guide for upgrading between minor versions. The pinned Zig toolchain in source distributions means that building from source requires Zig 0.16, which could be a barrier if you need to customize the build. The license is MIT, which is permissive and imposes few restrictions, but you should verify that the bundled libuv and any other dependencies have compatible licenses. The project depends on `opentelemetry-api` and `typing-extensions`, so you need to keep those up to date. The compatibility gates are a strong signal that the maintainers care about not breaking upstream libraries, but the cost of that is a large CI matrix that could slow down contributions. For a production deployment, you should pin the exact version and test after each upgrade.

Editorial conclusion

Adopt zuvloop if you run Python 3.14+ on a supported platform, rely heavily on asyncio scheduling or socket I/O, and want a drop-in replacement that preserves your existing Task and protocol code. Do not adopt it if you need Python versions below 3.14, require a stable API beyond version 0.0.x, or cannot tolerate potential regressions in edge cases that the compatibility gates may not cover. Before committing, verify your specific workloads against the benchmark suite in `benchmarks/`, confirm that your dependencies (especially those using custom event loop policies) work with the `loop_factory` pattern, and test under free-threaded CPython if you plan to use that build. The project is young; treat the performance numbers as directional until you reproduce them on your own hardware.

Frequently asked questions

Is asyncio built into Python?

The README describes zuvloop as a replacement for the built-in asyncio event loop, and the package requires Python 3.14 or newer. zuvloop is a drop-in substitute for that loop rather than a fork of the language, and it targets both standard and free-threaded CPython builds.

How do I use async event loops in Python with zuvloop?

Write ordinary asyncio code and run it with zuvloop.run(main()), or keep asyncio.run and pass the loop factory: asyncio.run(main(), loop_factory=zuvloop.new_event_loop). The same Task objects, protocols and APIs are used either way, and the loop installs with a single pip install zuvloop.

How does the zuvloop event loop work internally?

Scheduling, timers, sockets and DNS run in native code driven by libuv, the engine behind Node.js. The design notes describe argument storage inside handles so no tuple is allocated per callback, a native timer heap behind a single libuv timer handle, per-turn vectored write batching, zero-copy reads, and a fast path for name resolution of address literals.

Does zuvloop beat uvloop and rloop on every benchmark?

No. In the published table zuvloop leads eight of fourteen rows, rloop leads five, including call_soon, call_soon_threadsafe and both of the other timer rows, and uvloop leads one, the aiohttp client. The measurements are the median of seven interleaved in-process or five HTTP runs on a shared machine with background load, and the README asks readers to check the recorded variation first.

What does zuvloop require to build from source?

Python 3.14 or newer, and Zig 0.16 on the path for direct native development commands. A source distribution installs the pinned Zig toolchain inside its isolated build environment on its own, and prebuilt wheels exist for Linux on x86-64 and AArch64, macOS on x86-64 and arm64, and Windows on AMD64 and ARM64.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kludex-zuvloop.svg)](https://hysenlabs.com/projects/kludex-zuvloop)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/kludex-zuvloop.svg)](https://hysenlabs.com/projects/kludex-zuvloop)

Was this useful?