Skip to content

Websockets asyncio#152

Open
gonzalocasas wants to merge 15 commits into
mainfrom
websockets-asyncio
Open

Websockets asyncio#152
gonzalocasas wants to merge 15 commits into
mainfrom
websockets-asyncio

Conversation

@gonzalocasas

@gonzalocasas gonzalocasas commented May 30, 2026

Copy link
Copy Markdown
Contributor

Trying out an alternative, opt-in transport/event loop based on the asyncio implementation of Autobahn.

The new transport can be be manually selected via a transport kwargs on Ros, or set via set_default_transport, or with an environment variable.

I added some benchmarks as well. The benchmarks run on CI, the results are uploaded to the summary page of the CI task, here are the results of one of the latest runs. The conclusion is that the new transport has marginally higher topic throughput.

roslibpy transport benchmark

These numbers are sampled on shared CI infrastructure. Topic p95 and max
latencies can be noisy because of runner scheduling, Docker networking,
and rosbridge timing. Prefer medians and throughput for quick comparisons;
interpret tail latency across several runs.

Transport Metric Mean Median P95 Max Value
twisted initial connect 3.792 ms 3.792 ms 3.792 ms 3.792 ms
twisted get_time service 2.992 ms 2.966 ms 3.155 ms 6.852 ms
twisted topic round trip 9.314 ms 5.586 ms 30.082 ms 107.661 ms
twisted topic throughput 3383.0 msg/s
asyncio initial connect 3.278 ms 3.278 ms 3.278 ms 3.278 ms
asyncio get_time service 2.941 ms 2.907 ms 3.181 ms 4.231 ms
asyncio topic round trip 7.986 ms 6.135 ms 28.985 ms 33.600 ms
asyncio topic throughput 3557.1 msg/s
asyncio-uvloop initial connect 3.491 ms 3.491 ms 3.491 ms 3.491 ms
asyncio-uvloop get_time service 2.867 ms 2.850 ms 3.021 ms 3.525 ms
asyncio-uvloop topic round trip 9.701 ms 7.321 ms 27.646 ms 36.872 ms
asyncio-uvloop topic throughput 3726.2 msg/s
asyncio-no-compression initial connect 3.917 ms 3.917 ms 3.917 ms 3.917 ms
asyncio-no-compression get_time service 3.056 ms 3.011 ms 3.360 ms 4.174 ms
asyncio-no-compression topic round trip 7.642 ms 6.135 ms 20.870 ms 30.174 ms
asyncio-no-compression topic throughput 3585.0 msg/s
asyncio-uvloop-no-compression initial connect 3.452 ms 3.452 ms 3.452 ms 3.452 ms
asyncio-uvloop-no-compression get_time service 2.916 ms 2.897 ms 3.078 ms 4.561 ms
asyncio-uvloop-no-compression topic round trip 11.880 ms 6.798 ms 33.559 ms 131.025 ms
asyncio-uvloop-no-compression topic throughput 3267.9 msg/s

What type of change is this?

  • Bug fix in a backwards-compatible manner.
  • New feature in a backwards-compatible manner.
  • Breaking change: bug fix or new feature that involve incompatible API changes.
  • Other (e.g. doc update, configuration, etc)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I added a line to the CHANGELOG.rst file in the Unreleased section under the most fitting heading (e.g. Added, Changed, Removed).
  • I ran all tests on my computer and it's all green (i.e. invoke test).
  • I ran lint on my computer and there are no errors (i.e. invoke check).
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if appropriate)

@gonzalocasas gonzalocasas marked this pull request as ready for review May 30, 2026 15:00
@gonzalocasas

Copy link
Copy Markdown
Contributor Author

@RobotWebTools/roslibpy if anyone has comments on this, I would appreciate it!

@gonzalocasas

Copy link
Copy Markdown
Contributor Author

@RobotWebTools/roslibpy if anyone has comments on this, I would appreciate it!

/cc @EzraBrooks @MatthijsBurgh @chenkasirer @jihoonl

@chenkasirer chenkasirer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @gonzalocasas, seems that authobahn already has an asyncio transport option that can be used instead of twisted, so perhaps that's a better approach.

gonzalocasas and others added 2 commits June 23, 2026 14:10
The asyncio transport now runs the same Autobahn WebSocket stack as the
default transport, on an asyncio event loop instead of the Twisted reactor,
rather than depending on the standalone websockets library.

Because both transports build on Autobahn, whose txaio layer binds a single
async framework per process, the twisted and asyncio transports are mutually
exclusive within one process. To accommodate that:

- The back-compat RosBridgeClientFactory binding resolves lazily (PEP 562),
  so importing roslibpy no longer eagerly locks txaio to twisted before a
  transport is chosen.
- select_factory translates the deep txaio RuntimeError into a clear,
  actionable conflict message.
- Tests select a single transport per process via ROSLIBPY_TRANSPORT, and the
  ROS 1/2 CI workflows run the suite once per transport in separate processes.

The asyncio extra is dropped since no extra dependency is required beyond
Autobahn. Verified against live ROS 1 and ROS 2 rosbridge: asyncio matches
the twisted baseline test-for-test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gonzalocasas

Copy link
Copy Markdown
Contributor Author

Besides the benchmarks that CI runs, I did several runs locally with different settings, including a multi-iteration run against ROS2:

Transport benchmark — ROS 2, 6× run (high sample count)

Ran benchmarks/transport.py against a live ROS 2 (Kilted) rosbridge on
:9091 (rosbridge_websocket + rosapi_node + fibonacci action server),
6 iterations per case at 1500 service calls + 3000 topic round-trips
each (50-sample warmup), aggregating medians across runs.

Drive-by fix needed to run on ROS 2: the benchmark called Ros.get_time(),
which hard-codes ROS 1's secs/nsecs and KeyErrors on ROS 2. Replaced it
with a raw /rosapi/get_time round-trip (service_ping()) — same rosapi
service latency, now distro-agnostic.

Values are median [min–max] across the 6 runs:

case connect service med topic rtt med throughput
twisted 2.98 [1.73–7.36] ms 12.37 [11.1–16.1] ms 102.4 [62.4–133.1] ms 5274 [4877–5655] msg/s
asyncio 2.24 [1.71–5.41] ms 12.62 [11.1–15.8] ms 41.5 [25.6–125.7] ms 5201 [5032–5401] msg/s
asyncio-no-compression 3.25 [1.68–9.36] ms 12.56 [11.3–16.4] ms 44.5 [27.9–72.4] ms 4959 [4440–5411] msg/s
asyncio-uvloop 2.59 [1.94–3.42] ms 13.21 [11.0–16.6] ms 43.5 [30.3–76.5] ms 5036 [4317–5580] msg/s
asyncio-uvloop-no-compression 1.93 [1.67–3.83] ms 13.63 [11.1–16.7] ms 80.7 [35.9–128.8] ms 5004 [4746–5279] msg/s

Conclusions

  • Throughput parity, now with tight confidence. At this sample size all
    cases converge to ~5000–5275 msg/s with narrow, fully-overlapping ranges.
  • Service-call latency parity. All transports sit at ~12.4–13.6 ms
    (ROS 2's separate rosapi node + DDS round-trip; ~1.8 ms on ROS 1).
  • Burst topic RTT favors asyncio. Firing 3000 messages back-to-back —
    a queuing/backpressure-bound metric — twisted's median RTT is 102 ms vs
    asyncio's ~41–44 ms. Read as "asyncio handles burst backpressure at least
    as well as twisted, plausibly better"; tails overlap and it's a single 6× run,
    so suggestive rather than conclusive.
  • No measurable effect from uvloop or compression — neither beats plain
    asyncio here; both stay below the noise floor.

No regression on any axis. Consistent with the ROS 1 benchmark and with the
integration suites, which pass test-for-test identically on both transports
against live ROS 1 and ROS 2 bridges.

Comment thread .readthedocs.yaml
version: 2

build:
os: ubuntu-22.04

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not 24?

Comment thread .readthedocs.yaml
build:
os: ubuntu-22.04
tools:
python: "3.11"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a newer python?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants