rsloop 0.1.48__tar.gz → 0.1.49__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/tests.yml +1 -48
- rsloop-0.1.49/.github/workflows/vibeio-native.yml +109 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/Cargo.lock +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/Cargo.toml +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/PKG-INFO +75 -49
- {rsloop-0.1.48 → rsloop-0.1.49}/README.md +74 -48
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/README.md +16 -11
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/compare_event_loops.py +9 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/workload_matrix.py +2 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/development.md +12 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/justfile +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/pyproject.toml +9 -2
- {rsloop-0.1.48 → rsloop-0.1.49}/qualirs.toml +11 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/test-supported-pythons.sh +3 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/write_queue.rs +8 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/blocking/default.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/kqueue.rs +17 -2
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/mio.rs +19 -9
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/fs/mod.rs +4 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/buf.rs +7 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/splice.rs +2 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/util.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/lib.rs +2 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/tcp/listener.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/tcp/mod.rs +16 -22
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/tcp/stream.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/udp.rs +5 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/unix/mod.rs +16 -22
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/accept.rs +27 -20
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/accept_unix.rs +22 -16
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/connect.rs +84 -12
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/io_util.rs +44 -20
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/open.rs +2 -2
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/read.rs +58 -20
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/readv.rs +34 -37
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/recv.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/recvfrom.rs +23 -20
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/sendto.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/splice.rs +13 -4
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/waitpid.rs +1 -2
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/writeat.rs +1 -1
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/process/mod.rs +16 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/process/reaper.rs +15 -7
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/signal/unix.rs +20 -21
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/signal/windows.rs +7 -16
- rsloop-0.1.49/src/vibeio/test_support.rs +281 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/time/sleep.rs +2 -2
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/time/timeout.rs +3 -6
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/timer.rs +41 -34
- rsloop-0.1.49/tests/test_benchmark_loops.py +141 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/uv.lock +15 -3
- {rsloop-0.1.48 → rsloop-0.1.49}/.cargo/config.toml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/anyio.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/kani.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/mkdocs.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/update-version.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.github/workflows/wheels.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.gitignore +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.python-version +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/.vscode/settings.json +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/LICENSE +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/check_regression.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/idle_statistics.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/runtime.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/timer.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/benches/vibeio-performance.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/build.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/examples.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/getting-started.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/how-it-works.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/index.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/project-structure.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/rsloop.png +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/rust-extensions.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/verification.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/vibeio-cleanup.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/docs/vibeio-findings.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/01_basics.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/02_fd_and_sockets.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/03_streams.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/04_unix_and_accepted_socket.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/05_pipes_signals_subprocesses.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/README.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/fastapi_service.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/picows_server.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/picows_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/examples/wsbench_websockets.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/mkdocs.yml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/pyrightconfig.json +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/__init__.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/__init__.pyi +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/_bootstrap.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/_loop.pyi +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/_loop_compat.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/_profile.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/_run.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/python/rsloop/py.typed +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/rust-toolchain.toml +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/build-pgo-wheels.sh +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/build-wheels.sh +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/generate_test_tls_certs.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/prepare_anyio_rsloop_tests.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/python-versions.sh +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/run_python_tests.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/scripts/run_rust_tests.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/async_event.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/asyncgens.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/asyncio_cache.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/connections.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/executor.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/ffi_helpers.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/lifecycle.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/methods.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/pipes.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/pre_exec.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/process_handles.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/process_spawn.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/process_stdio.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/servers.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/signals.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/sock_ops.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/socket_connect.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/sockets.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/spawn_env.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/tasks.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/tls_params.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api/watchers.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/loop_api.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/bindings/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/blocking.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/build_metadata.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/context.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/callbacks.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/commands.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/dispatcher/timer_entry.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/dispatcher.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/loop_core.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/engine/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/errors.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/lib.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/module_init.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/platform/fd/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/platform/fd/windows.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/platform/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/platform/windows_vibeio.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/profiler.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/python_names.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/rust_async.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/core_events.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/core_protocol.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/core_state.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/params.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/py_pipes.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/py_transport.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/spawn.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/process/worker.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/accept.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/buffers.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/builder.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/connect.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/core_events.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/core_protocol.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/core_state.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/core_write.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/fast.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/io_targets.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/pipe_transport.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/platform.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/poll.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/protocol.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/py_server.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/py_transport.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/reader.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/reader_task.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/server.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/server_core.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/server_types.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/socket_transport.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/stats.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/tls_session.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/tls_transport.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/tuning.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/worker.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/stream/writer.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/tls/material.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/transport/tls/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/verification.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/UPSTREAM.md +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/blocking/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/builder.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/iocp.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/mock.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/driver/uring.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/executor.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/fd_inner.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/fs/file.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/fs/metadata.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/fs/open_options.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/pipe.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/io/stdio.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/unix/listener.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/net/unix/stream.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/fsync.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/hard_link.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/mkdir.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/readat.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/readiness.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/rename.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/send.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/socket_addr.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/statx.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/symlink.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/unlink.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/write.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/op/writev.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/signal/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/task.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/time/interval.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/time/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/util/async_wrap.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/src/vibeio/util/mod.rs +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/_smoke.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/aiohttp_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/anyio_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/beanie_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/daphne_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/django_asgi_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/django_orm_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/edgy_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/falcon_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/fastapi_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/faststream_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/hypercorn_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/litestar_granian_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/litestar_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/mangum_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/ormar_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/piccolo_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/quart_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/sanic_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/sqlalchemy_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/sqlmodel_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/starlette_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/tortoise_orm_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/packages/uvicorn_test.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/proptest-regressions/transport/stream/fast.txt +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_compat.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_free_threading.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_idle_benchmark.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_public_api.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_run.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_stream_reader.py +0 -0
- {rsloop-0.1.48 → rsloop-0.1.49}/tests/test_tls.py +0 -0
|
@@ -62,54 +62,7 @@ jobs:
|
|
|
62
62
|
run: python3 scripts/run_rust_tests.py ${{ matrix.test_args }}
|
|
63
63
|
|
|
64
64
|
vibeio-native:
|
|
65
|
-
|
|
66
|
-
runs-on: ${{ matrix.os }}
|
|
67
|
-
timeout-minutes: 15
|
|
68
|
-
strategy:
|
|
69
|
-
fail-fast: false
|
|
70
|
-
matrix:
|
|
71
|
-
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
72
|
-
steps:
|
|
73
|
-
- name: Check out repository
|
|
74
|
-
uses: actions/checkout@v6
|
|
75
|
-
with:
|
|
76
|
-
persist-credentials: false
|
|
77
|
-
|
|
78
|
-
- name: Set up Rust
|
|
79
|
-
uses: dtolnay/rust-toolchain@stable
|
|
80
|
-
with:
|
|
81
|
-
toolchain: "1.98.1"
|
|
82
|
-
components: clippy, rustfmt
|
|
83
|
-
|
|
84
|
-
- name: Cache runtime harness build
|
|
85
|
-
uses: Swatinem/rust-cache@v2
|
|
86
|
-
with:
|
|
87
|
-
workspaces: tools/vibeio-check
|
|
88
|
-
|
|
89
|
-
- name: Check embedded runtime formatting
|
|
90
|
-
run: cargo fmt --manifest-path tools/vibeio-check/Cargo.toml --all -- --check
|
|
91
|
-
|
|
92
|
-
- name: Check embedded runtime documentation
|
|
93
|
-
env:
|
|
94
|
-
RUSTDOCFLAGS: -D warnings
|
|
95
|
-
run: cargo doc --manifest-path tools/vibeio-check/Cargo.toml --all-features --no-deps --locked
|
|
96
|
-
|
|
97
|
-
- name: Lint embedded runtime on the native target
|
|
98
|
-
run: cargo clippy --manifest-path tools/vibeio-check/Cargo.toml --all-targets --all-features --locked -- -D warnings
|
|
99
|
-
|
|
100
|
-
- name: Check isolated embedded runtime features
|
|
101
|
-
shell: bash
|
|
102
|
-
run: |
|
|
103
|
-
for feature in '' fs process signal pipe stdio splice blocking-default; do
|
|
104
|
-
cargo clippy --manifest-path tools/vibeio-check/Cargo.toml --all-targets --no-default-features --features "$feature" --locked -- -D warnings
|
|
105
|
-
cargo test --manifest-path tools/vibeio-check/Cargo.toml --no-default-features --features "$feature" --locked
|
|
106
|
-
done
|
|
107
|
-
|
|
108
|
-
- name: Run default-feature embedded runtime tests
|
|
109
|
-
run: cargo test --manifest-path tools/vibeio-check/Cargo.toml --locked
|
|
110
|
-
|
|
111
|
-
- name: Run all-feature embedded runtime tests
|
|
112
|
-
run: cargo test --manifest-path tools/vibeio-check/Cargo.toml --all-features --locked
|
|
65
|
+
uses: ./.github/workflows/vibeio-native.yml
|
|
113
66
|
|
|
114
67
|
clippy:
|
|
115
68
|
name: clippy (risk-focused)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Vibeio native tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
repetitions:
|
|
7
|
+
description: "All-feature test repetitions per native platform"
|
|
8
|
+
type: choice
|
|
9
|
+
default: "3"
|
|
10
|
+
options: ["1", "3", "10"]
|
|
11
|
+
isolated_features:
|
|
12
|
+
description: "Also lint and test each feature independently"
|
|
13
|
+
type: boolean
|
|
14
|
+
default: true
|
|
15
|
+
workflow_call:
|
|
16
|
+
inputs:
|
|
17
|
+
repetitions:
|
|
18
|
+
type: string
|
|
19
|
+
default: "1"
|
|
20
|
+
isolated_features:
|
|
21
|
+
type: boolean
|
|
22
|
+
default: true
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
native:
|
|
29
|
+
name: vibeio (${{ matrix.os }})
|
|
30
|
+
runs-on: ${{ matrix.os }}
|
|
31
|
+
timeout-minutes: 30
|
|
32
|
+
strategy:
|
|
33
|
+
fail-fast: false
|
|
34
|
+
matrix:
|
|
35
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
36
|
+
defaults:
|
|
37
|
+
run:
|
|
38
|
+
shell: bash
|
|
39
|
+
env:
|
|
40
|
+
RUST_BACKTRACE: "1"
|
|
41
|
+
CARGO_TERM_COLOR: never
|
|
42
|
+
REPETITIONS: ${{ inputs.repetitions }}
|
|
43
|
+
steps:
|
|
44
|
+
- name: Check out repository
|
|
45
|
+
uses: actions/checkout@v6
|
|
46
|
+
with:
|
|
47
|
+
persist-credentials: false
|
|
48
|
+
|
|
49
|
+
- name: Set up Rust
|
|
50
|
+
uses: dtolnay/rust-toolchain@stable
|
|
51
|
+
with:
|
|
52
|
+
toolchain: "1.98.1"
|
|
53
|
+
components: clippy, rustfmt
|
|
54
|
+
|
|
55
|
+
- name: Cache runtime harness build
|
|
56
|
+
uses: Swatinem/rust-cache@v2
|
|
57
|
+
with:
|
|
58
|
+
workspaces: tools/vibeio-check
|
|
59
|
+
|
|
60
|
+
- name: Record native environment
|
|
61
|
+
run: |
|
|
62
|
+
mkdir -p target/vibeio-native-logs
|
|
63
|
+
case "$REPETITIONS" in
|
|
64
|
+
1|3|10) ;;
|
|
65
|
+
*) echo "Unsupported repetition count: $REPETITIONS"; exit 1 ;;
|
|
66
|
+
esac
|
|
67
|
+
{
|
|
68
|
+
git rev-parse HEAD
|
|
69
|
+
rustc -Vv
|
|
70
|
+
cargo -V
|
|
71
|
+
uname -a
|
|
72
|
+
} 2>&1 | tee target/vibeio-native-logs/environment.log
|
|
73
|
+
|
|
74
|
+
- name: Check formatting and documentation
|
|
75
|
+
env:
|
|
76
|
+
RUSTDOCFLAGS: -D warnings
|
|
77
|
+
run: |
|
|
78
|
+
cargo fmt --manifest-path tools/vibeio-check/Cargo.toml --all -- --check
|
|
79
|
+
cargo doc --manifest-path tools/vibeio-check/Cargo.toml --all-features --no-deps --locked 2>&1 | tee target/vibeio-native-logs/documentation.log
|
|
80
|
+
|
|
81
|
+
- name: Lint native all-feature build
|
|
82
|
+
run: cargo clippy --manifest-path tools/vibeio-check/Cargo.toml --all-targets --all-features --locked -- -D warnings 2>&1 | tee target/vibeio-native-logs/clippy.log
|
|
83
|
+
|
|
84
|
+
- name: Inventory native tests
|
|
85
|
+
run: cargo test --manifest-path tools/vibeio-check/Cargo.toml --all-features --locked -- --list 2>&1 | tee target/vibeio-native-logs/test-inventory.log
|
|
86
|
+
|
|
87
|
+
- name: Run native all-feature tests repeatedly
|
|
88
|
+
run: |
|
|
89
|
+
for ((iteration=1; iteration<=REPETITIONS; iteration++)); do
|
|
90
|
+
cargo test --manifest-path tools/vibeio-check/Cargo.toml --all-features --locked -- --nocapture 2>&1 | tee "target/vibeio-native-logs/all-features-${iteration}.log"
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
- name: Check isolated features
|
|
94
|
+
if: ${{ inputs.isolated_features }}
|
|
95
|
+
run: |
|
|
96
|
+
for feature in '' fs process signal pipe stdio splice blocking-default; do
|
|
97
|
+
label="${feature:-default}"
|
|
98
|
+
cargo clippy --manifest-path tools/vibeio-check/Cargo.toml --all-targets --no-default-features --features "$feature" --locked -- -D warnings 2>&1 | tee "target/vibeio-native-logs/clippy-${label}.log"
|
|
99
|
+
cargo test --manifest-path tools/vibeio-check/Cargo.toml --no-default-features --features "$feature" --locked -- --nocapture 2>&1 | tee "target/vibeio-native-logs/tests-${label}.log"
|
|
100
|
+
done
|
|
101
|
+
|
|
102
|
+
- name: Upload native test evidence
|
|
103
|
+
if: always()
|
|
104
|
+
uses: actions/upload-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: vibeio-native-${{ matrix.os }}-${{ github.run_attempt }}
|
|
107
|
+
path: target/vibeio-native-logs/
|
|
108
|
+
if-no-files-found: warn
|
|
109
|
+
retention-days: 14
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rsloop
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.49
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -496,10 +496,32 @@ The repository also includes:
|
|
|
496
496
|
|
|
497
497
|
```bash
|
|
498
498
|
uv run --with maturin maturin develop --release
|
|
499
|
-
uv run --with uvloop python benches/compare_event_loops.py
|
|
499
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py
|
|
500
500
|
```
|
|
501
501
|
|
|
502
|
-
|
|
502
|
+
### Four-loop comparison on Linux
|
|
503
|
+
|
|
504
|
+
Measured on September 7, 2026 on an Intel Core i9-9900K, Linux
|
|
505
|
+
7.0.0-31-generic (x86_64), and CPython 3.14, with rsloop 0.1.48 built in
|
|
506
|
+
release mode. Each entry is the median of five measured runs after one warmup,
|
|
507
|
+
with each run in a fresh subprocess. Times are milliseconds; lower is better.
|
|
508
|
+
|
|
509
|
+
| Workload | asyncio | uvloop | zuvloop | rsloop |
|
|
510
|
+
|---|---:|---:|---:|---:|
|
|
511
|
+
| 200,000 callbacks | 109.24 | 51.03 | **36.97** | 45.80 |
|
|
512
|
+
| 50,000 tasks | 148.07 | 88.56 | **77.48** | 87.05 |
|
|
513
|
+
| 5,000 TCP roundtrips | 149.27 | 122.88 | 105.12 | **90.62** |
|
|
514
|
+
|
|
515
|
+
The TCP workload uses 1,024-byte payloads and rsloop's native fast streams;
|
|
516
|
+
the other loops use stdlib asyncio streams. Use `--no-rsloop-fast-streams`
|
|
517
|
+
to compare all loops through the stdlib streams layer. Zuvloop led callbacks
|
|
518
|
+
and tasks in this run, while rsloop led TCP roundtrips. These are local
|
|
519
|
+
microbenchmarks, not isolated-lab measurements or general application performance
|
|
520
|
+
claims; do not compare them directly with the historical macOS results below.
|
|
521
|
+
|
|
522
|
+
### Historical macOS comparison
|
|
523
|
+
|
|
524
|
+
An earlier example output from the script on macOS (arm64) with CPython 3.14:
|
|
503
525
|
|
|
504
526
|
```
|
|
505
527
|
callbacks (200,000 ops)
|
|
@@ -525,51 +547,51 @@ The production-shaped workload matrix exercises HTTP, WebSocket libraries,
|
|
|
525
547
|
TLS, mixed message sizes, backpressure, and connection lifecycle behavior:
|
|
526
548
|
|
|
527
549
|
```bash
|
|
528
|
-
uv run --with uvloop python benches/workload_matrix.py \
|
|
529
|
-
--loops rsloop,uvloop \
|
|
550
|
+
uv run --with uvloop --with zuvloop python benches/workload_matrix.py \
|
|
551
|
+
--loops rsloop,uvloop,zuvloop \
|
|
530
552
|
--sustained \
|
|
531
|
-
--
|
|
553
|
+
--scenarios http_keepalive,tls_http,websocket_messages,websocket_tls,websockets_messages,websockets_tls,aiohttp_websocket_messages,aiohttp_websocket_tls,starlette_websocket_messages,starlette_websocket_tls,mixed_streams,bulk_transfer \
|
|
554
|
+
--json-output target/matrix-zuvloop.json
|
|
532
555
|
```
|
|
533
556
|
|
|
534
|
-
Measured on September
|
|
535
|
-
7.0.0-31-generic (x86_64), CPython 3.14.
|
|
536
|
-
uvloop 0.22.1. Each row reports the median of seven measured runs after two
|
|
557
|
+
Measured on September 7, 2026 with an Intel Core i9-9900K, Linux
|
|
558
|
+
7.0.0-31-generic (x86_64), CPython 3.14.7, rsloop 0.1.48 (release build),
|
|
559
|
+
uvloop 0.22.1, and zuvloop 0.0.14. Each row reports the median of seven measured runs after two
|
|
537
560
|
warmups, using 16 concurrent connections and 500 requests per
|
|
538
561
|
connection. Throughput is traffic-only operations per second, except for
|
|
539
562
|
`bulk_transfer`, which reports traffic MiB/s. The p95 columns are the medians
|
|
540
|
-
of each run's p95 latency
|
|
563
|
+
of each run's p95 latency. Higher throughput and lower latency are better.
|
|
564
|
+
Each loop/scenario pair runs in its own subprocess, with warmups and measured
|
|
565
|
+
runs sharing that process. Loops run sequentially in the order shown.
|
|
541
566
|
|
|
542
|
-
WebSocket library versions were websockets 17.
|
|
567
|
+
WebSocket library versions were websockets 17.1, aiohttp 3.14.3, Starlette
|
|
543
568
|
1.6.0, and uvicorn 0.52.3. The run used unrestricted CPU affinity, with other
|
|
544
569
|
host services running but no concurrent builds or tests. These measurements
|
|
545
570
|
are from a different host than the macOS microbenchmark example above.
|
|
546
571
|
|
|
547
|
-
| Scenario | rsloop | uvloop |
|
|
548
|
-
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
549
|
-
| HTTP keep-alive |
|
|
550
|
-
| TLS HTTP |
|
|
551
|
-
| Raw WebSocket |
|
|
552
|
-
| Raw WebSocket over TLS |
|
|
553
|
-
| `websockets` |
|
|
554
|
-
| `websockets` over TLS | 26,
|
|
555
|
-
| aiohttp WebSocket |
|
|
556
|
-
| aiohttp WebSocket over TLS |
|
|
557
|
-
| Starlette WebSocket | 18,
|
|
558
|
-
| Starlette WebSocket over TLS |
|
|
559
|
-
| Mixed streams |
|
|
560
|
-
| Bulk transfer (MiB/s) |
|
|
572
|
+
| Scenario | rsloop | uvloop | zuvloop | rsloop p95 | uvloop p95 | zuvloop p95 |
|
|
573
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
574
|
+
| HTTP keep-alive | 54,357 | 48,364 | 56,826 | 0.335 ms | 0.366 ms | 0.297 ms |
|
|
575
|
+
| TLS HTTP | 69,047 | 25,267 | 24,076 | 0.287 ms | 0.663 ms | 0.700 ms |
|
|
576
|
+
| Raw WebSocket | 4,834 | 4,858 | 4,932 | 5.244 ms | 3.878 ms | 3.381 ms |
|
|
577
|
+
| Raw WebSocket over TLS | 4,875 | 4,457 | 4,421 | 4.425 ms | 4.020 ms | 3.770 ms |
|
|
578
|
+
| `websockets` | 23,878 | 26,237 | 27,928 | 0.784 ms | 0.640 ms | 0.606 ms |
|
|
579
|
+
| `websockets` over TLS | 26,946 | 14,933 | 13,640 | 0.640 ms | 1.103 ms | 1.319 ms |
|
|
580
|
+
| aiohttp WebSocket | 31,145 | 34,552 | 37,733 | 0.625 ms | 0.498 ms | 0.458 ms |
|
|
581
|
+
| aiohttp WebSocket over TLS | 33,730 | 18,576 | 18,245 | 0.517 ms | 0.905 ms | 0.929 ms |
|
|
582
|
+
| Starlette WebSocket | 18,992 | 20,695 | 21,176 | 0.964 ms | 0.816 ms | 0.873 ms |
|
|
583
|
+
| Starlette WebSocket over TLS | 17,876 | 13,454 | 12,487 | 0.955 ms | 1.243 ms | 1.437 ms |
|
|
584
|
+
| Mixed streams | 41,260 | 35,805 | 37,094 | 0.483 ms | 0.504 ms | 0.476 ms |
|
|
585
|
+
| Bulk transfer (MiB/s) | 2,120.0 | 1,269.6 | 1,287.8 | 14.114 ms | 25.134 ms | 24.801 ms |
|
|
561
586
|
|
|
562
587
|
The former single-burst idle-activation row has been retired: its traffic
|
|
563
588
|
phase lasted only a few milliseconds and produced unstable throughput rankings.
|
|
564
589
|
Idle activation now has a separate, versioned latency benchmark described below.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
19.4%, and 22.3%, respectively. They still trail uvloop. The historical regression
|
|
571
|
-
gate also flagged HTTP tail latency and legacy idle activation; this is not an
|
|
572
|
-
across-the-board performance win. See the
|
|
590
|
+
In this run, zuvloop had the highest plaintext HTTP and WebSocket throughput,
|
|
591
|
+
while rsloop led TLS throughput, mixed streams, and bulk transfer. Throughput
|
|
592
|
+
and tail latency do not always agree: rsloop's raw WebSocket p95 was higher
|
|
593
|
+
than both alternatives, including over TLS. These results are not an
|
|
594
|
+
across-the-board performance win or a before/after regression measurement. See the
|
|
573
595
|
[benchmark documentation](./benches/README.md) for workload definitions and
|
|
574
596
|
reproduction commands.
|
|
575
597
|
|
|
@@ -582,41 +604,45 @@ so has more to lose when cores are contended.
|
|
|
582
604
|
### Idle activation latency
|
|
583
605
|
|
|
584
606
|
```bash
|
|
585
|
-
|
|
586
|
-
--loops rsloop,uvloop --scenarios idle_connections --repeat
|
|
607
|
+
uv run --with uvloop --with zuvloop python benches/workload_matrix.py \
|
|
608
|
+
--loops rsloop,uvloop,zuvloop --scenarios idle_connections --repeat 9 \
|
|
587
609
|
--idle-cycles 100 --idle-warmup-cycles 5 --idle-seconds 0.2 \
|
|
588
610
|
--json-output target/idle-v2-paired.json
|
|
589
611
|
```
|
|
590
612
|
|
|
591
613
|
Idle v2 reuses 200 established connections across repeated idle/wakeup cycles.
|
|
592
614
|
It measures all replies from one shared activation timestamp, including task
|
|
593
|
-
scheduling delay, and reports first/50%/95%/all-reply latency.
|
|
594
|
-
blocks
|
|
615
|
+
scheduling delay, and reports first/50%/95%/all-reply latency. Nine fresh-process
|
|
616
|
+
blocks rotate loop order so each loop runs first three times; confidence intervals resample whole paired runs,
|
|
595
617
|
not individual connections. Results are classified as improved, regressed, or
|
|
596
618
|
inconclusive using a 5% practical threshold and an approximate 95% confidence
|
|
597
|
-
interval. The command takes about
|
|
619
|
+
interval. The command takes about ten minutes; use `--idle-cycles 3
|
|
598
620
|
--idle-warmup-cycles 1 --idle-seconds 0.01 --repeat 1` for a smoke test only.
|
|
599
621
|
|
|
600
622
|
The new measurements cannot be compared with the retired ops/s row. See
|
|
601
623
|
[benchmark methodology and regression handling](./benches/README.md#idle-activation-v2)
|
|
602
624
|
for timing definitions, host controls, raw distributions, and sample requirements.
|
|
603
625
|
|
|
604
|
-
|
|
605
|
-
|
|
626
|
+
Measured on September 7, 2026 on the Linux/i9-9900K host above with CPython
|
|
627
|
+
3.14.7, rsloop 0.1.48 (release), uvloop 0.22.1, and zuvloop 0.0.14.
|
|
628
|
+
The run collected 900 measured cycles per loop in 27 distinct processes,
|
|
629
|
+
with unrestricted affinity and no concurrent
|
|
606
630
|
builds or test runs. These are medians across runs of each run's median cycle
|
|
607
631
|
milestone, in milliseconds (lower is better):
|
|
608
632
|
|
|
609
633
|
| Loop | First reply | 50% replied | 95% replied | All replied |
|
|
610
634
|
| --- | ---: | ---: | ---: | ---: |
|
|
611
|
-
| rsloop |
|
|
612
|
-
| uvloop |
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
635
|
+
| rsloop | 12.605 | 12.868 | 13.059 | 13.146 |
|
|
636
|
+
| uvloop | 13.564 | 13.927 | 14.219 | 14.247 |
|
|
637
|
+
| zuvloop | 16.331 | 16.772 | 17.146 | 17.206 |
|
|
638
|
+
|
|
639
|
+
Both paired comparisons against uvloop are **inconclusive**. The geometric mean
|
|
640
|
+
run-level p95 latency change is -13.7% for rsloop, with a 95% bootstrap interval
|
|
641
|
+
of [-27.5%, +3.1%], and -0.4% for zuvloop, with an interval of [-21.5%, +25.6%].
|
|
642
|
+
These use paired run ratios, not the ratio of the table's medians. Individual
|
|
643
|
+
cycle-p95 latencies span 3.444–29.529 ms for rsloop, 3.739–24.965 ms for uvloop,
|
|
644
|
+
and 3.388–29.540 ms for zuvloop. The benchmark exposes that uncertainty rather
|
|
645
|
+
than declaring a latency winner from the median ordering alone.
|
|
620
646
|
|
|
621
647
|
See [`benches/README.md`](./benches/README.md) for workload details and
|
|
622
648
|
extra flags, and [`examples/README.md`](./examples/README.md) for the FastAPI
|
|
@@ -464,10 +464,32 @@ The repository also includes:
|
|
|
464
464
|
|
|
465
465
|
```bash
|
|
466
466
|
uv run --with maturin maturin develop --release
|
|
467
|
-
uv run --with uvloop python benches/compare_event_loops.py
|
|
467
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py
|
|
468
468
|
```
|
|
469
469
|
|
|
470
|
-
|
|
470
|
+
### Four-loop comparison on Linux
|
|
471
|
+
|
|
472
|
+
Measured on September 7, 2026 on an Intel Core i9-9900K, Linux
|
|
473
|
+
7.0.0-31-generic (x86_64), and CPython 3.14, with rsloop 0.1.48 built in
|
|
474
|
+
release mode. Each entry is the median of five measured runs after one warmup,
|
|
475
|
+
with each run in a fresh subprocess. Times are milliseconds; lower is better.
|
|
476
|
+
|
|
477
|
+
| Workload | asyncio | uvloop | zuvloop | rsloop |
|
|
478
|
+
|---|---:|---:|---:|---:|
|
|
479
|
+
| 200,000 callbacks | 109.24 | 51.03 | **36.97** | 45.80 |
|
|
480
|
+
| 50,000 tasks | 148.07 | 88.56 | **77.48** | 87.05 |
|
|
481
|
+
| 5,000 TCP roundtrips | 149.27 | 122.88 | 105.12 | **90.62** |
|
|
482
|
+
|
|
483
|
+
The TCP workload uses 1,024-byte payloads and rsloop's native fast streams;
|
|
484
|
+
the other loops use stdlib asyncio streams. Use `--no-rsloop-fast-streams`
|
|
485
|
+
to compare all loops through the stdlib streams layer. Zuvloop led callbacks
|
|
486
|
+
and tasks in this run, while rsloop led TCP roundtrips. These are local
|
|
487
|
+
microbenchmarks, not isolated-lab measurements or general application performance
|
|
488
|
+
claims; do not compare them directly with the historical macOS results below.
|
|
489
|
+
|
|
490
|
+
### Historical macOS comparison
|
|
491
|
+
|
|
492
|
+
An earlier example output from the script on macOS (arm64) with CPython 3.14:
|
|
471
493
|
|
|
472
494
|
```
|
|
473
495
|
callbacks (200,000 ops)
|
|
@@ -493,51 +515,51 @@ The production-shaped workload matrix exercises HTTP, WebSocket libraries,
|
|
|
493
515
|
TLS, mixed message sizes, backpressure, and connection lifecycle behavior:
|
|
494
516
|
|
|
495
517
|
```bash
|
|
496
|
-
uv run --with uvloop python benches/workload_matrix.py \
|
|
497
|
-
--loops rsloop,uvloop \
|
|
518
|
+
uv run --with uvloop --with zuvloop python benches/workload_matrix.py \
|
|
519
|
+
--loops rsloop,uvloop,zuvloop \
|
|
498
520
|
--sustained \
|
|
499
|
-
--
|
|
521
|
+
--scenarios http_keepalive,tls_http,websocket_messages,websocket_tls,websockets_messages,websockets_tls,aiohttp_websocket_messages,aiohttp_websocket_tls,starlette_websocket_messages,starlette_websocket_tls,mixed_streams,bulk_transfer \
|
|
522
|
+
--json-output target/matrix-zuvloop.json
|
|
500
523
|
```
|
|
501
524
|
|
|
502
|
-
Measured on September
|
|
503
|
-
7.0.0-31-generic (x86_64), CPython 3.14.
|
|
504
|
-
uvloop 0.22.1. Each row reports the median of seven measured runs after two
|
|
525
|
+
Measured on September 7, 2026 with an Intel Core i9-9900K, Linux
|
|
526
|
+
7.0.0-31-generic (x86_64), CPython 3.14.7, rsloop 0.1.48 (release build),
|
|
527
|
+
uvloop 0.22.1, and zuvloop 0.0.14. Each row reports the median of seven measured runs after two
|
|
505
528
|
warmups, using 16 concurrent connections and 500 requests per
|
|
506
529
|
connection. Throughput is traffic-only operations per second, except for
|
|
507
530
|
`bulk_transfer`, which reports traffic MiB/s. The p95 columns are the medians
|
|
508
|
-
of each run's p95 latency
|
|
531
|
+
of each run's p95 latency. Higher throughput and lower latency are better.
|
|
532
|
+
Each loop/scenario pair runs in its own subprocess, with warmups and measured
|
|
533
|
+
runs sharing that process. Loops run sequentially in the order shown.
|
|
509
534
|
|
|
510
|
-
WebSocket library versions were websockets 17.
|
|
535
|
+
WebSocket library versions were websockets 17.1, aiohttp 3.14.3, Starlette
|
|
511
536
|
1.6.0, and uvicorn 0.52.3. The run used unrestricted CPU affinity, with other
|
|
512
537
|
host services running but no concurrent builds or tests. These measurements
|
|
513
538
|
are from a different host than the macOS microbenchmark example above.
|
|
514
539
|
|
|
515
|
-
| Scenario | rsloop | uvloop |
|
|
516
|
-
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
517
|
-
| HTTP keep-alive |
|
|
518
|
-
| TLS HTTP |
|
|
519
|
-
| Raw WebSocket |
|
|
520
|
-
| Raw WebSocket over TLS |
|
|
521
|
-
| `websockets` |
|
|
522
|
-
| `websockets` over TLS | 26,
|
|
523
|
-
| aiohttp WebSocket |
|
|
524
|
-
| aiohttp WebSocket over TLS |
|
|
525
|
-
| Starlette WebSocket | 18,
|
|
526
|
-
| Starlette WebSocket over TLS |
|
|
527
|
-
| Mixed streams |
|
|
528
|
-
| Bulk transfer (MiB/s) |
|
|
540
|
+
| Scenario | rsloop | uvloop | zuvloop | rsloop p95 | uvloop p95 | zuvloop p95 |
|
|
541
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
542
|
+
| HTTP keep-alive | 54,357 | 48,364 | 56,826 | 0.335 ms | 0.366 ms | 0.297 ms |
|
|
543
|
+
| TLS HTTP | 69,047 | 25,267 | 24,076 | 0.287 ms | 0.663 ms | 0.700 ms |
|
|
544
|
+
| Raw WebSocket | 4,834 | 4,858 | 4,932 | 5.244 ms | 3.878 ms | 3.381 ms |
|
|
545
|
+
| Raw WebSocket over TLS | 4,875 | 4,457 | 4,421 | 4.425 ms | 4.020 ms | 3.770 ms |
|
|
546
|
+
| `websockets` | 23,878 | 26,237 | 27,928 | 0.784 ms | 0.640 ms | 0.606 ms |
|
|
547
|
+
| `websockets` over TLS | 26,946 | 14,933 | 13,640 | 0.640 ms | 1.103 ms | 1.319 ms |
|
|
548
|
+
| aiohttp WebSocket | 31,145 | 34,552 | 37,733 | 0.625 ms | 0.498 ms | 0.458 ms |
|
|
549
|
+
| aiohttp WebSocket over TLS | 33,730 | 18,576 | 18,245 | 0.517 ms | 0.905 ms | 0.929 ms |
|
|
550
|
+
| Starlette WebSocket | 18,992 | 20,695 | 21,176 | 0.964 ms | 0.816 ms | 0.873 ms |
|
|
551
|
+
| Starlette WebSocket over TLS | 17,876 | 13,454 | 12,487 | 0.955 ms | 1.243 ms | 1.437 ms |
|
|
552
|
+
| Mixed streams | 41,260 | 35,805 | 37,094 | 0.483 ms | 0.504 ms | 0.476 ms |
|
|
553
|
+
| Bulk transfer (MiB/s) | 2,120.0 | 1,269.6 | 1,287.8 | 14.114 ms | 25.134 ms | 24.801 ms |
|
|
529
554
|
|
|
530
555
|
The former single-burst idle-activation row has been retired: its traffic
|
|
531
556
|
phase lasted only a few milliseconds and produced unstable throughput rankings.
|
|
532
557
|
Idle activation now has a separate, versioned latency benchmark described below.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
19.4%, and 22.3%, respectively. They still trail uvloop. The historical regression
|
|
539
|
-
gate also flagged HTTP tail latency and legacy idle activation; this is not an
|
|
540
|
-
across-the-board performance win. See the
|
|
558
|
+
In this run, zuvloop had the highest plaintext HTTP and WebSocket throughput,
|
|
559
|
+
while rsloop led TLS throughput, mixed streams, and bulk transfer. Throughput
|
|
560
|
+
and tail latency do not always agree: rsloop's raw WebSocket p95 was higher
|
|
561
|
+
than both alternatives, including over TLS. These results are not an
|
|
562
|
+
across-the-board performance win or a before/after regression measurement. See the
|
|
541
563
|
[benchmark documentation](./benches/README.md) for workload definitions and
|
|
542
564
|
reproduction commands.
|
|
543
565
|
|
|
@@ -550,41 +572,45 @@ so has more to lose when cores are contended.
|
|
|
550
572
|
### Idle activation latency
|
|
551
573
|
|
|
552
574
|
```bash
|
|
553
|
-
|
|
554
|
-
--loops rsloop,uvloop --scenarios idle_connections --repeat
|
|
575
|
+
uv run --with uvloop --with zuvloop python benches/workload_matrix.py \
|
|
576
|
+
--loops rsloop,uvloop,zuvloop --scenarios idle_connections --repeat 9 \
|
|
555
577
|
--idle-cycles 100 --idle-warmup-cycles 5 --idle-seconds 0.2 \
|
|
556
578
|
--json-output target/idle-v2-paired.json
|
|
557
579
|
```
|
|
558
580
|
|
|
559
581
|
Idle v2 reuses 200 established connections across repeated idle/wakeup cycles.
|
|
560
582
|
It measures all replies from one shared activation timestamp, including task
|
|
561
|
-
scheduling delay, and reports first/50%/95%/all-reply latency.
|
|
562
|
-
blocks
|
|
583
|
+
scheduling delay, and reports first/50%/95%/all-reply latency. Nine fresh-process
|
|
584
|
+
blocks rotate loop order so each loop runs first three times; confidence intervals resample whole paired runs,
|
|
563
585
|
not individual connections. Results are classified as improved, regressed, or
|
|
564
586
|
inconclusive using a 5% practical threshold and an approximate 95% confidence
|
|
565
|
-
interval. The command takes about
|
|
587
|
+
interval. The command takes about ten minutes; use `--idle-cycles 3
|
|
566
588
|
--idle-warmup-cycles 1 --idle-seconds 0.01 --repeat 1` for a smoke test only.
|
|
567
589
|
|
|
568
590
|
The new measurements cannot be compared with the retired ops/s row. See
|
|
569
591
|
[benchmark methodology and regression handling](./benches/README.md#idle-activation-v2)
|
|
570
592
|
for timing definitions, host controls, raw distributions, and sample requirements.
|
|
571
593
|
|
|
572
|
-
|
|
573
|
-
|
|
594
|
+
Measured on September 7, 2026 on the Linux/i9-9900K host above with CPython
|
|
595
|
+
3.14.7, rsloop 0.1.48 (release), uvloop 0.22.1, and zuvloop 0.0.14.
|
|
596
|
+
The run collected 900 measured cycles per loop in 27 distinct processes,
|
|
597
|
+
with unrestricted affinity and no concurrent
|
|
574
598
|
builds or test runs. These are medians across runs of each run's median cycle
|
|
575
599
|
milestone, in milliseconds (lower is better):
|
|
576
600
|
|
|
577
601
|
| Loop | First reply | 50% replied | 95% replied | All replied |
|
|
578
602
|
| --- | ---: | ---: | ---: | ---: |
|
|
579
|
-
| rsloop |
|
|
580
|
-
| uvloop |
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
603
|
+
| rsloop | 12.605 | 12.868 | 13.059 | 13.146 |
|
|
604
|
+
| uvloop | 13.564 | 13.927 | 14.219 | 14.247 |
|
|
605
|
+
| zuvloop | 16.331 | 16.772 | 17.146 | 17.206 |
|
|
606
|
+
|
|
607
|
+
Both paired comparisons against uvloop are **inconclusive**. The geometric mean
|
|
608
|
+
run-level p95 latency change is -13.7% for rsloop, with a 95% bootstrap interval
|
|
609
|
+
of [-27.5%, +3.1%], and -0.4% for zuvloop, with an interval of [-21.5%, +25.6%].
|
|
610
|
+
These use paired run ratios, not the ratio of the table's medians. Individual
|
|
611
|
+
cycle-p95 latencies span 3.444–29.529 ms for rsloop, 3.739–24.965 ms for uvloop,
|
|
612
|
+
and 3.388–29.540 ms for zuvloop. The benchmark exposes that uncertainty rather
|
|
613
|
+
than declaring a latency winner from the median ordering alone.
|
|
588
614
|
|
|
589
615
|
See [`benches/README.md`](./benches/README.md) for workload details and
|
|
590
616
|
extra flags, and [`examples/README.md`](./examples/README.md) for the FastAPI
|
|
@@ -8,14 +8,15 @@ before/after results and limitations; these are not Python event-loop comparison
|
|
|
8
8
|
This benchmark compares:
|
|
9
9
|
|
|
10
10
|
- stdlib `asyncio`
|
|
11
|
-
- `uvloop`
|
|
11
|
+
- `uvloop` (`winloop` on Windows)
|
|
12
|
+
- `zuvloop` on Python 3.14+
|
|
12
13
|
- the Rust prototype in `rsloop`
|
|
13
14
|
|
|
14
15
|
Run it from the repository root so Python resolves the editable Rust package cleanly:
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
18
|
uv run --with maturin maturin develop --release
|
|
18
|
-
uv run --with uvloop python benches/compare_event_loops.py
|
|
19
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
Benchmark runner: [`benches/compare_event_loops.py`](./compare_event_loops.py)
|
|
@@ -28,7 +29,7 @@ Useful quick run:
|
|
|
28
29
|
|
|
29
30
|
```bash
|
|
30
31
|
uv run --with maturin maturin develop --release
|
|
31
|
-
uv run --with uvloop python benches/compare_event_loops.py \
|
|
32
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py \
|
|
32
33
|
--warmups 0 \
|
|
33
34
|
--repeat 3 \
|
|
34
35
|
--callbacks 50000 \
|
|
@@ -42,7 +43,7 @@ measured runs, add a label directory:
|
|
|
42
43
|
|
|
43
44
|
```bash
|
|
44
45
|
uv run --with maturin maturin develop --release --features profiler
|
|
45
|
-
uv run --with uvloop python benches/compare_event_loops.py \
|
|
46
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py \
|
|
46
47
|
--loops rsloop \
|
|
47
48
|
--workloads callbacks,tasks,tcp_streams \
|
|
48
49
|
--profile-rsloop-dir benches/profiles
|
|
@@ -68,10 +69,10 @@ Current workloads:
|
|
|
68
69
|
- `tcp_streams`: local `asyncio.start_server()` / `asyncio.open_connection()` echo round trips
|
|
69
70
|
|
|
70
71
|
By default, `tcp_streams` uses `rsloop`'s native fast streams. If you want all
|
|
71
|
-
|
|
72
|
+
selected loops to go through the stdlib `asyncio` streams layer instead, pass:
|
|
72
73
|
|
|
73
74
|
```bash
|
|
74
|
-
uv run --with uvloop python benches/compare_event_loops.py --no-rsloop-fast-streams
|
|
75
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/compare_event_loops.py --no-rsloop-fast-streams
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
## Representative workload matrix
|
|
@@ -107,18 +108,22 @@ Build rsloop in release mode and run the standard matrix with:
|
|
|
107
108
|
|
|
108
109
|
```bash
|
|
109
110
|
uv run --with maturin maturin develop --release
|
|
110
|
-
uv run --with uvloop python benches/workload_matrix.py # Unix
|
|
111
|
-
uv run --with winloop python benches/workload_matrix.py # Windows
|
|
111
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/workload_matrix.py # Unix
|
|
112
|
+
uv run --with winloop --with 'zuvloop; python_version >= "3.14"' python benches/workload_matrix.py # Windows
|
|
112
113
|
```
|
|
113
114
|
|
|
114
115
|
The default comparison is `asyncio,uvloop,rsloop` on Unix. Because uvloop is
|
|
115
116
|
not available on Windows, the default there is `asyncio,winloop,rsloop`.
|
|
116
|
-
|
|
117
|
+
On Python 3.14+, zuvloop is added before rsloop in both defaults. Older Python
|
|
118
|
+
versions retain the three-loop defaults. Select a subset with `--loops`, for
|
|
119
|
+
example `--loops uvloop,zuvloop,rsloop` on Unix with Python 3.14+.
|
|
120
|
+
Unavailable optional loops are reported and skipped; workload failures are not
|
|
121
|
+
silently replaced by another loop. zuvloop is a benchmark-only dependency.
|
|
117
122
|
|
|
118
123
|
For a quick smoke run:
|
|
119
124
|
|
|
120
125
|
```bash
|
|
121
|
-
uv run --with uvloop python benches/workload_matrix.py \
|
|
126
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/workload_matrix.py \
|
|
122
127
|
--warmups 0 \
|
|
123
128
|
--repeat 1 \
|
|
124
129
|
--concurrency 4 \
|
|
@@ -152,7 +157,7 @@ pass before each rsloop scenario:
|
|
|
152
157
|
|
|
153
158
|
```bash
|
|
154
159
|
uv run --with maturin maturin develop --release --features profiler
|
|
155
|
-
uv run --with uvloop python benches/workload_matrix.py \
|
|
160
|
+
uv run --with uvloop --with 'zuvloop; python_version >= "3.14"' python benches/workload_matrix.py \
|
|
156
161
|
--loops rsloop \
|
|
157
162
|
--profile-rsloop-dir benches/profiles \
|
|
158
163
|
--allow-profiler-build
|