Expose Multi-Segment / Scatter-Gather Payload APIs in zenoh-python#732
Open
BigTailFox wants to merge 1 commit into
Open
Expose Multi-Segment / Scatter-Gather Payload APIs in zenoh-python#732BigTailFox wants to merge 1 commit into
BigTailFox wants to merge 1 commit into
Conversation
…rt multipart payload and potential zero-copy path between python buffer object and zenoh-python.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This commit contains a prototype implementation for #730.
It adds a
ZBytes.from_segments(..., copy=False)path that can construct aZBytespayload from multiple immutable Python byte segments without copying the segment contents into Zenoh-owned memory.The main motivation is to support efficient integration with serialization libraries that naturally expose multipart payloads, such as Cap’n Proto via
msg.to_segments().Motivation
Some Python serialization stacks produce payloads as multiple byte segments instead of one contiguous
bytesobject.For example, Cap’n Proto can serialize a message as:
where
segmentsis a list of Pythonbytes.Before this change, passing those segments into Zenoh still required copying the serialized payload into Zenoh-owned memory. For large camera frames, tensors, or robotics simulation payloads, this extra copy can be a significant part of the publisher-side latency.
The proposed
copy=Falsemode allowsZBytesto retain references to the original immutable Python segment owners and expose them as Zenoh payload slices, avoiding this additional copy.Example Usage
Performance Evaluation
I benchmarked the new
ZBytes.from_segments(..., copy=False)path with a Cap’n Proto tensor message payload.The test payload is a
1920 x 1536int32tensor, serialized as a Cap’n Proto message:to_segments()output:bytesBenchmark Results
For constructing
ZBytesfrom Cap’n Proto segments:In this benchmark, the zero-copy path reduces the
ZBytesconstruction cost from approximately 4.1 ms to approximately 0.007 ms for an 11.25 MiB payload.This changes the cost model from being proportional to the total payload size to being proportional to the number of segments.
End-to-End Context
In the tested Cap’n Proto path, the relevant publisher-side payload construction pipeline is:
The measured
msg.to_segments()cost is approximately:With
copy=True, the path is approximately:With
copy=False, the path becomes:Therefore, for this image-sized raw tensor payload, this prototype removes approximately 4 ms of publisher-side memory copy overhead, reducing this part of the payload construction path by roughly 45%.
This is particularly useful for robotics simulation workloads where large camera images, segmentation masks, depth maps, or tensor payloads are published at high frequency.
Scope and Limitations
This is not a full end-to-end zero-copy path from the original NumPy / Torch tensor memory into the Zenoh transport layer.
In the Cap’n Proto case,
msg.to_segments()still materializes Pythonbytessegments. This PR specifically removes the additional copy from:So the optimization is still valuable for existing Python serialization libraries that already expose immutable byte segments.
Lifetime and Safety Model
The intended safety model is:
copy=TrueZBytesis independent of the original Python objects.copy=FalseZBytesretains ownership references to the Python segment objects.For the initial prototype, I intentionally kept the supported input types conservative. The safest zero-copy case is immutable Python
bytes, because the memory cannot be mutated after theZByteshas been created.Mutable buffers such as
bytearrayor writablememoryvieware more subtle because the application could mutate the payload while Zenoh is still using it. I would like maintainer feedback on what input types should be accepted forcopy=False.Questions for Maintainers
I would appreciate feedback on the following points before turning this prototype into a final implementation:
1. API shape
Is
ZBytes.from_segments(segments, copy=False)the right API, or would maintainers prefer a more explicit name for the borrowed / retained-owner path?For example:
2. Supported input types for
copy=FalseShould the initial implementation only accept immutable
bytes, or should it also accept read-onlymemoryviewobjects when they are C-contiguous and byte-addressable?My current preference is to start conservatively with immutable / read-only inputs only.
3. Fallback behavior
If an unsupported buffer type is passed with
copy=False, should the function:My current preference is to raise an explicit error, because silent fallback would make performance unpredictable.
4. Lifetime guarantees
Does retaining Python object owners inside
ZBytesmatch the expected Zenoh payload lifetime model?The goal is to ensure that the Python segment owners remain alive until Zenoh no longer references the payload, including asynchronous
publisher.put(...)use cases.5. Documentation expectations
Are there specific Zenoh Python documentation conventions I should follow for documenting the difference between
copy=Trueandcopy=False?6. Test coverage
Besides basic round-trip tests, I plan to add tests for:
bytessegmentscopy=Truevscopy=Falsebehaviorpayload.segments()copy=FalsePlease let me know if there are additional edge cases maintainers would like covered.
Summary
This prototype shows that avoiding the extra Python-segments-to-Zenoh copy can save around 4 ms for an 11.25 MiB Cap’n Proto payload.
The implementation is intended to be a conservative and practical zero-copy bridge for serialization libraries that already expose immutable multipart byte segments.
I would appreciate maintainer feedback on the API shape, accepted input types, fallback behavior, and lifetime model before polishing this into a final PR.