Skip to content

Scalable Active Message Multicast - #465

Open
lightsighter wants to merge 3 commits into
mainfrom
mbauer-fix-multicast
Open

Scalable Active Message Multicast#465
lightsighter wants to merge 3 commits into
mainfrom
mbauer-fix-multicast

Conversation

@lightsighter

Copy link
Copy Markdown
Contributor

Motivation

ActiveMessage(NodeSet) was multicast in name only. Each network backend expanded the target set at the source and issued one send per target:

for(NodeID tgt : targets) { ... internal->commit_message(...); }

So a message to N peers cost the originating node N sends, and the target list travelled the wire as a raw node set. This adds a real multicast facility — bounded-radix forwarding over ordinary unicast, with an adaptive target encoding — and deletes the per-target source loops from all four backends.

What changed

A standalone target-set codec — src/realm/multicast.{h,cc}

MulticastTargetSet stores sorted, disjoint, non-adjacent runs, so a range covering thousands of nodes costs one entry and partition() slices it without ever expanding to individual IDs. EncodedMulticastTargets picks among eight wire forms — EMPTY, SINGLE, SMALL_INLINE, RANGES, DELTA_LIST, BITMAP, ALL_NODES, ALL_EXCEPT — by computing the actual serialized byte size of every candidate and taking the minimum, not by a density heuristic. Decoding validates cardinality against both the remaining payload and the configured node count before allocating or looping, plus node bounds, range overflow, canonical ordering and overlong varints; malformed input returns a status rather than aborting, so it stays unit-testable.

This layer deliberately has no dependency on activemsg.h, which is what lets its tests run with no network, no runtime and no message table.

Forwarding, folded into the active-message layer — activemsg.{h,inl,cc}

The origin removes itself from the target set, partitions the remainder into at most R near-equal slices, and sends one envelope to the first node of each. A relay validates it is in its own slice, removes itself, then enqueues child envelopes before invoking the local handler — forward-before-deliver, which matters for messages like runtime shutdown whose handler stops progress. Final delivery presents origin_node as the handler's sender, not the last relay.

Depth is O(log_R M) with O(M) total edges. Handler redispatch reuses the existing ActiveMessageHandlerTable / IncomingMessageManager machinery rather than duplicating signature detection, and large envelopes ride the existing fragmentation path on every hop.

Optional aggregate remote completion uses a transient acknowledgement tree: each relay holds its parent and outstanding-child count, acks once, and immediately reclaims. Fire-and-forget multicasts carry no ack metadata and create no state, and no reusable target plan survives a completed multicast.

Call sites migrated: runtime shutdown (runtime_impl.cc), metadata invalidation (metadata.cc), instance requests (inst_impl.cc), CUDA and HIP IPC.

Removed: ActiveMessage(NodeSet), Network::create_active_message_impl(NodeSet, …), the NetworkModule NodeSet virtuals, the NodeSet recommended_max_payload overloads, and the backend multicast constructors and source loops — 13 files, +51 / −669. Realm::NodeSet itself is unchanged; only the multicast-by-NodeSet path is gone.

Testing

101 new unit tests. Every encoding is forced with an explicit kind assertion, and a shared helper independently recomputes the minimum across all eight candidates to confirm the encoder actually picked it. Coverage includes malformed and fuzzed payloads (verified against a guard page to catch out-of-bounds reads), NodeID 0 and max, partition balance/disjointness/exact-union across radices 1–64, forward-before-deliver ordering, origin-sender preservation across multiple hops, fragmented payloads, and completion firing exactly once.

@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.82143% with 170 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.94%. Comparing base (a5ca775) to head (0b6d486).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/realm/activemsg.cc 78.22% 75 Missing and 23 partials ⚠️
src/realm/multicast.cc 92.49% 36 Missing and 10 partials ⚠️
src/realm/activemsg.inl 42.85% 8 Missing ⚠️
src/realm/runtime_impl.cc 0.00% 8 Missing ⚠️
src/realm/inst_impl.cc 0.00% 5 Missing ⚠️
src/realm/metadata.cc 0.00% 3 Missing ⚠️
src/realm/activemsg.h 80.00% 1 Missing ⚠️
src/realm/multicast.h 95.45% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #465      +/-   ##
==========================================
+ Coverage   30.41%   34.94%   +4.53%     
==========================================
  Files         199      202       +3     
  Lines       41281    44162    +2881     
  Branches    14942    15266     +324     
==========================================
+ Hits        12554    15433    +2879     
+ Misses      28272    27383     -889     
- Partials      455     1346     +891     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@eddy16112

Copy link
Copy Markdown
Contributor

I do not think we should force every network module to use tree based broadcast. IMHO, we could provide an abstraction function (bool support_multicast(...)) to le each network module to report if it could provide native support for multicast, e.g. hardware multicast support. If it is support, we fallback to use radix tree.

@lightsighter

Copy link
Copy Markdown
Contributor Author

I do not think we should force every network module to use tree based broadcast. IMHO, we could provide an abstraction function (bool support_multicast(...)) to le each network module to report if it could provide native support for multicast, e.g. hardware multicast support. If it is support, we fallback to use radix tree.

To be clear, at the moment, none of our networking layers have native hardware/driver level support for multi-cast active messages which is why the tree broadcast logic all lives on the "client" side of the Network module interface in this pull request. There's no custom logic in any of the different Network module backends for doing the multicast broadcast tree. The only changes in the different network module backends have been removing some dead code that is no longer necessary with the change to the multicast interface. In the future, if we ever had true hardware level support for multicast then I agree we would need a network module API call for checking for multicast support and pushing that functionality down into the Network module backends that support it.

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.

2 participants