1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Shard digests for the reshard refit path.
A refit that reports plausible timings can still install the wrong bytes: the wire
can deliver correctly while the *plan* points a copy at the wrong sub-box, and
generation-level metrics such as KL against a reference are too coarse to localise
that. This module supplies the cheap half of the answer - a digest a publisher can
compute over the shard it owns, and which a receiver can recompute over the bytes it
actually received - so a mismatch names the offending tensor instead of showing up
as a slightly worse loss curve three steps later.
Why not a byte hash: hashing 30 GB per rank per refit on the GPU means either a
device-side hash kernel we do not have, or a copy to host we cannot afford. The
digest below is a *position-sensitive* reduction instead - see :func:`tensor_digest`
for why plain sums are not enough.
This module is the digest and the wire format for it. The receiver-side gate that
recomputes and compares is deliberately not here; it needs the fresh-discovery
refresh to avoid reporting ordinary training updates as corruption, which is its own
change. Publishing is opt-in via ``MX_RESHARD_PUBLISH_DIGEST`` because the reduction
costs a pass over every published tensor.
Scope, because it is narrower than "the refit is verified" and the difference matters
when planning the consumer. A publisher digest covers a whole shard, so a receiver can
compare it directly only when the ordinary plan gives that receiver every byte of the
shard. That includes a coarser inference topology which assembles whole trainer shards
into known destination sub-boxes. A finer inference topology instead reads only part of
each trainer shard, so its partial destination has no value comparable to the published
whole-shard digest.
For strict qualification of that partial-read case, the receiver must full-pull and
verify the publisher's whole shard, derive the expected destination slice from the
verified staging buffer, and compare it with the destination produced by the ordinary
exact-segment read. Both reads must target the same immutable, step-stamped source
version. :func:`shard_region` serves the full-pull reference path; the direct case needs
the transfer plan's source-to-destination provenance to locate each complete publisher
shard in the assembled destination. A fingerprint over the contributing publishers is
useful freshness evidence, but it is not a byte-for-byte expectation for a partial read
and must not be presented as one.
"""
=
# Words per digest row. Row sums are position-sensitive *between* rows, since the row
# vector is hashed in order, so this is the granularity at which a permutation is
# detected: reordering whole 4 KiB rows is caught, reordering words inside one row is
# not. 1024 int32 words keeps the hashed vector ~4 KiB per MiB of tensor, small enough
# to move to host, while staying coarse enough that the reduction is a single fast
# kernel.
= 1024
= - 1
# Position weights are the same vector for every shard of a given row count, and a
# refit digests thousands of shards, so they are built once per (length, device)
# rather than per call.
: =
"""Cached ``[1..length]`` on ``device``, for the position-weighted reduction."""
=
=
=
=
return
"""A position-sensitive digest of ``tensor``'s bytes, computed on-device.
Plain reductions (sum, sum-of-squares) are unusable here because the failure
mode we most need to catch is a *permutation*: a plan that copies the right
bytes to the wrong offsets preserves every order-independent statistic. So the
bytes are viewed as int32 words, reshaped into rows, reduced per row, and the
resulting row vector is hashed **in order** - reordering rows changes the hash,
while the reduction itself stays one cheap kernel over the full tensor.
The tensor is made contiguous first, since a strided view has no meaningful byte
order to digest, and is rebased to a word-aligned start. Returns a hex string so it
can ride in JSON alongside the shard table.
"""
=
=
# What a publisher actually passes. ``narrow`` returns a view that is
# contiguous *and* offset, so the branch above never fires, and
# ``.contiguous()`` would return self anyway - only a copy rebases the start.
# Without this the int32 view below raises on any shard whose byte offset is
# not a multiple of 4, which is every odd bf16 split: the gate/up narrow at an
# odd half, the QKV narrow at an odd row.
#
# Rebasing rather than skipping the leading bytes to reach an aligned
# boundary, which would be cheaper and wrong: the digest has to be a function
# of the shard's contents alone. A publisher digesting at offset 2818 and a
# receiver digesting the same bytes at offset 0 must agree, and they only do
# if neither one's result depends on where the bytes happen to sit.
=
=
=
# int32 view needs a 4-byte-aligned length; digest the aligned body as words and
# fold the remainder in separately rather than silently dropping it.
= -
=
= //
# Reduce all the way to scalars on-device. Moving the row vector to the host
# instead would mean a per-element Python conversion for every one of the
# thousands of shards in a refit, which is slower than the transfer being
# verified. Position sensitivity survives the second reduction because the row
# sums are weighted by their index before being summed; int64 overflow simply
# makes it arithmetic mod 2**64, which is still order-dependent.
=
# dtype=int64 accumulates without materialising an int64 copy of the input.
=
=
=
=
# The ragged bytes past the last whole int32 word. Folded in explicitly
# because dropping them would make two tensors differing only in their final
# bytes digest identically.
=
return
"""The sub-box of an assembled full-pull staging buffer that one publisher owns.
``full_tensor`` is the flat staging buffer holding the whole logical source
tensor; a publisher's shard occupies the box at ``shard_offset`` with ``shape``.
Returned as a contiguous copy because :func:`tensor_digest` digests byte order
and the box is generally strided inside the full tensor.
Here rather than with the comparison gate because it is the inverse of what a
publisher digests: the publisher hashes its own contiguous shard, and this is how
a receiver recovers the same box out of the buffer it assembled.
The three ranks must agree. A short ``shard_offset`` would otherwise stop the
narrow loop early and return a larger region than the publisher digested, which
surfaces as a mismatch on a shard that transferred correctly - the most expensive
kind of wrong answer for a gate whose whole job is to be trusted.
"""
=
=
return
"""``tensor_digest`` when digest publication is on, otherwise ``None``.
Read live rather than captured at import so a publisher can be switched without
reimporting, and so the tests do not have to reload this module.
"""
return None
return