Skip to content

FEA[DE]: replay_increment, writing a delta back at its source slots - #477

Merged
shijieliu merged 10 commits into
NVIDIA:mainfrom
jiashuy:feat/dynamicemb-replay-increment
Sep 10, 2026
Merged

shijieliu merged 10 commits into
NVIDIA:mainfrom
jiashuy:feat/dynamicemb-replay-increment

Conversation

@jiashuy

@jiashuy jiashuy commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator

incremental_dump produces a delta; there was no way to apply one. Replay is the other half of that pipeline: a training job dumps periodically, the delta is shipped, and a replica catches up without reloading a full checkpoint.

Write-back is by slot. Every key goes to the slot and value row it held in the source, leaving the target layout-identical to it, which is what lets the two converge rather than merely agree on contents. A key is only ever probed inside its own home bucket, so that requires the two tables to share a layout; replay_increment compares the delta's meta (capacity, bucket_capacity, num_scores, world_size, and the table_options fields score_strategy / dim / dist_type) and raises before writing anything if they differ. score_strategy is compared by physical word order, since (TIMESTAMP, LFU) and (LFU, TIMESTAMP) are the same layout on device. The kernel enforces the same rule per key: a slot outside its key's home bucket, or held by another writer, leaves that key unplaced and the host raises rather than silently dropping it.

A delta now carries the whole stored row. values is embeddings only, with the rest of the value row in optimizer_states and every score word in scores, both at the width the file checkpoint uses -- rowwise Adagrad reserves 16 bytes per row in the fused layout but fills one scalar, so dumping the runtime width would ship padding and disagree with DynamicEmbDump about what a row is. Timestamp score columns travel as an age, since %globaltimer is per-device. ReplayContent picks which of the three to write back: a serving replica wants the embedding alone, a training replica resuming from its source wants all three.

Removals are split in two. A key leaves a table by being evicted to make room or by being erased explicitly, and only the second is a removal a replica has to perform -- an eviction is reproduced by whoever takes over the slot, which is in the same delta. They are retained in separate buffers, so a replay can tell them apart instead of erasing keys that were merely evicted. Retention is configured at different times for the same reason: retaining evictions swaps in a collecting insert kernel and costs a device sync per evicting insert, so it is a table option, while an erase already holds its keys and takes its own EvictedItemMode per call. EvictedItemMode is a flag so further kinds of retention can be added without revisiting call sites.

The cache needs explicit invalidation. Writing a slot reproduces an eviction in the storage but not in the cache, which is a separate index the write does not reach -- and flush_cache pushes every cached key back down, so a survivor would be resurrected into storage by the next dump. Replay flushes first, then drops the delta's keys, its erased keys, and its evicted keys from the cache.

MurmurHash3's finalizer had four copies. It is now one on each side of the language boundary: src/murmur_hash.cuh for device code, murmur3_fmix64 for host. The copy in test_hash_roundrobin_kuairand.py stays deliberately separate, since an oracle that calls the code under test stops being one.

Description

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@jiashuy jiashuy changed the title feat(dynamicemb): replay_increment, writing a delta back at its sourc… FEA[DE]: replay_increment, writing a delta back at its source slots Sep 2, 2026
@jiashuy

jiashuy commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@greptile-apps

greptile-apps Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with no outstanding findings or new issues introduced since the previous review.

Summary

  • Replays embeddings, optimizer state, scores, and explicit erasures into layout-compatible replicas.
  • Adds preflight validation to prevent partial multi-table application.
  • Preserves source slot layout and validates per-key slot placement.
  • Separates retained evictions from explicit erasures and handles cache invalidation.
  • Consolidates MurmurHash3 finalization and adds replay, retention, distributed, and storage tests.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Training DynamicEmb table] --> B[incremental_dump]
    B --> C[DeltaDumpResult]
    C --> D{Validate every target table}
    D -->|Mismatch| E[Raise before writes]
    D -->|Compatible| F[Flush target cache]
    F --> G[Apply explicit erasures]
    G --> H[Write keys and embeddings at source slots]
    H --> I[Optionally restore optimizer state and scores]
    I --> J[Invalidate replayed, erased, and displaced cache keys]
    J --> K[Layout-aligned replica]
Loading

Reviews (7) · Last reviewed commit: "Merge branch 'main' into feat/dynamicemb..."

Comment thread corelib/dynamicemb/dynamicemb/key_value_table.py
Comment thread corelib/dynamicemb/dynamicemb/batched_dynamicemb_tables.py
@JacoCheung

JacoCheung commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator

❌ Pipeline #65820599 -- failed

Job Status Log
pre_check ❌ failed view
train_build_x86 ❌ failed view
train_build_arm64 ❌ failed view
prepare-jet-b200-smoke ⏩ skipped view
prepare-jet-b200-inference ⏩ skipped view
prepare-jet-cw-dfw-e2e-benchmark ⏩ skipped view
build_whl ⏩ skipped view
dynamicemb_test_fwd_bwd_8gpus ⏩ skipped view
dynamicemb_test_load_dump_8gpus ⏩ skipped view
unit_test_1gpu_a100 ⏩ skipped view
unit_test_1gpu_h100 ⏩ skipped view
unit_test_4gpu ⏩ skipped view
unit_test_tp_4gpu ⏩ skipped view
L20_unit_test_1gpu ⏩ skipped view
inference_unit_test_1gpu ⏩ skipped view
inference_test_1gpu ⏩ skipped view
inference_test_nve_2605_1gpu ⏩ skipped view

Result: 0/3 jobs passed

View full pipeline

@jiashuy

jiashuy commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

❌ Pipeline #65954897 -- failed

Job Status Log
pre_check ❌ failed view
train_build_x86 ❌ failed view
train_build_arm64 ❌ failed view
prepare-jet-b200-smoke ⏩ skipped view
prepare-jet-b200-inference ⏩ skipped view
prepare-jet-cw-dfw-e2e-benchmark ⏩ skipped view
build_whl ⏩ skipped view
dynamicemb_test_fwd_bwd_8gpus ⏩ skipped view
dynamicemb_test_load_dump_8gpus ⏩ skipped view
unit_test_1gpu_a100 ⏩ skipped view
unit_test_1gpu_h100 ⏩ skipped view
unit_test_4gpu ⏩ skipped view
unit_test_tp_4gpu ⏩ skipped view
L20_unit_test_1gpu ⏩ skipped view
inference_unit_test_1gpu ⏩ skipped view
inference_test_1gpu ⏩ skipped view
inference_test_nve_2605_1gpu ⏩ skipped view

Result: 0/3 jobs passed

View full pipeline

@jiashuy

jiashuy commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

❌ Pipeline #65990206 -- failed

Job Status Log
pre_check ❌ failed view
train_build_x86 ✅ success view
train_build_arm64 ✅ success view
prepare-jet-b200-smoke ✅ success view
prepare-jet-b200-inference ✅ success view
prepare-jet-cw-dfw-e2e-benchmark ✅ success view
build_whl ✅ success view
dynamicemb_test_fwd_bwd_8gpus ✅ success view
dynamicemb_test_load_dump_8gpus ❌ failed view
unit_test_1gpu_a100 ❌ failed view
unit_test_1gpu_h100 ✅ success view
unit_test_4gpu ❌ failed view
unit_test_tp_4gpu ❌ failed view
L20_unit_test_1gpu ✅ success view
inference_unit_test_1gpu ❌ failed view
inference_test_1gpu ❌ failed view
inference_test_nve_2605_1gpu ✅ success view

Result: 10/17 jobs passed

View full pipeline

…e slots

incremental_dump produces a delta; there was no way to apply one. Replay is
the other half of that pipeline: a training job dumps periodically, the delta
is shipped, and a replica catches up without reloading a full checkpoint.

Write-back is by slot. Every key goes to the slot and value row it held in the
source, leaving the target layout-identical to it, which is what lets the two
converge rather than merely agree on contents. A key is only ever probed inside
its own home bucket, so that requires the two tables to share a layout;
replay_increment compares the delta's meta (capacity, bucket_capacity,
num_scores, world_size, and the table_options fields score_strategy / dim /
dist_type) and raises before writing anything if they differ. score_strategy is
compared by physical word order, since (TIMESTAMP, LFU) and (LFU, TIMESTAMP)
are the same layout on device. The kernel enforces the same rule per key: a
slot outside its key's home bucket, or held by another writer, leaves that key
unplaced and the host raises rather than silently dropping it.

A delta now carries the whole stored row. values is embeddings only, with the
rest of the value row in optimizer_states and every score word in scores, both
at the width the file checkpoint uses -- rowwise Adagrad reserves 16 bytes per
row in the fused layout but fills one scalar, so dumping the runtime width
would ship padding and disagree with DynamicEmbDump about what a row is.
Timestamp score columns travel as an age, since %globaltimer is per-device.
ReplayContent picks which of the three to write back: a serving replica wants
the embedding alone, a training replica resuming from its source wants all
three.

Removals are split in two. A key leaves a table by being evicted to make room
or by being erased explicitly, and only the second is a removal a replica has
to perform -- an eviction is reproduced by whoever takes over the slot, which
is in the same delta. They are retained in separate buffers, so a replay can
tell them apart instead of erasing keys that were merely evicted. Retention is
configured at different times for the same reason: retaining evictions swaps in
a collecting insert kernel and costs a device sync per evicting insert, so it
is a table option, while an erase already holds its keys and takes its own
EvictedItemMode per call. EvictedItemMode is a flag so further kinds of
retention can be added without revisiting call sites.

The cache needs explicit invalidation. Writing a slot reproduces an eviction in
the storage but not in the cache, which is a separate index the write does not
reach -- and flush_cache pushes every cached key back down, so a survivor would
be resurrected into storage by the next dump. Replay flushes first, then drops
the delta's keys, its erased keys, and its evicted keys from the cache.

MurmurHash3's finalizer had four copies. It is now one on each side of the
language boundary: src/murmur_hash.cuh for device code, murmur3_fmix64 for
host. The copy in test_hash_roundrobin_kuairand.py stays deliberately separate,
since an oracle that calls the code under test stops being one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jiashuy
jiashuy force-pushed the feat/dynamicemb-replay-increment branch from 0da9b02 to f5af24e Compare September 4, 2026 06:30
@jiashuy

jiashuy commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

❌ Pipeline #66187052 -- failed

Job Status Log
pre_check ❌ failed view
train_build_arm64 ✅ success view
prepare-jet-b200-smoke ✅ success view
prepare-jet-b200-inference ✅ success view
prepare-jet-cw-dfw-e2e-benchmark ⏩ skipped view
build_whl ⏩ skipped view
dynamicemb_test_fwd_bwd_8gpus ⏩ skipped view
dynamicemb_test_load_dump_8gpus ⏩ skipped view
unit_test_1gpu_a100 ⏩ skipped view
unit_test_1gpu_h100 ⏩ skipped view
unit_test_4gpu ⏩ skipped view
unit_test_tp_4gpu ⏩ skipped view
L20_unit_test_1gpu ⏩ skipped view
inference_unit_test_1gpu ⏩ skipped view
inference_test_1gpu ⏩ skipped view
inference_test_nve_2605_1gpu ⏩ skipped view
train_build_x86 ❌ failed view

Result: 3/5 jobs passed

View full pipeline

@jiashuy

jiashuy commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 7, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Pipeline #66552826 -- success

Job Status Log
pre_check ❌ failed view
train_build_x86 ✅ success view
train_build_arm64 ✅ success view
prepare-jet-b200-smoke ✅ success view
prepare-jet-b200-inference ✅ success view
prepare-jet-cw-dfw-e2e-benchmark ✅ success view
build_whl ✅ success view
dynamicemb_test_fwd_bwd_8gpus ✅ success view
dynamicemb_test_load_dump_8gpus ✅ success view
unit_test_1gpu_a100 ✅ success view
unit_test_1gpu_h100 ✅ success view
unit_test_4gpu ✅ success view
unit_test_tp_4gpu ❌ failed view
L20_unit_test_1gpu ✅ success view
inference_unit_test_1gpu ✅ success view
inference_test_1gpu ✅ success view
inference_test_nve_2605_1gpu ✅ success view

Result: 15/17 jobs passed

View full pipeline

jiashuy and others added 4 commits September 7, 2026 07:47
…by pg

replay_increment kept only the keys a rank owns, recomputing ownership as
hash(key) % world_size. It took both the rank and the world size from the
process group the delta had been gathered over, which is not what the tables
were sharded across.

That contradicted the rest of the module. meta["world_size"] is documented as
the global WORLD the source was sharded over, explicitly not the gather pg;
_replay_compatibility compares it against self._shard_world_size; and
owned_key_mask's own docstring says ownership follows the target's fan-out. So
the compatibility check validated one number and the filter then used another.

With pg a strict subgroup of WORLD -- an intra-node group over two nodes, say
-- a replay filtered by 8 against a table sharded by 16, using a node-local
rank. Every key was claimed by several ranks and none by the rank that owns it:
duplicated writes and lost keys, silently. The existing distributed test passes
intra_and_cross_node_pg()[0] but runs on a single node, where that group is
WORLD, so nothing caught it.

Ownership now comes from dist.get_rank() and _shard_world_size.

Drop the pg argument from replay_increment at both the module and model level.
Once the filter stops using it, nothing else does: replay is local -- filter by
ownership, then write -- with no collective to scope. Leaving an inert
parameter would invite the reading that passing a subgroup narrows the replay,
which is precisely the bug. incremental_dump keeps its pg, where it really does
scope the all_gather.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…w writes

The module asked three layout questions of a storage -- how much capacity a
table has, how wide a hash bucket is, how many score words a key carries -- and
each answered by branching on hasattr(storage, "key_index_map") to tell a
single-tier storage from a hybrid one. Four such branches, with the capacity
one written out twice.

HybridStorage already exposes its tiers as .tables; give DynamicEmbStorage the
same property, returning its one state. The branches then have nothing left to
decide: capacity sums over the tiers, bucket capacity and score-word count read
tiers[0]. The distinction stops leaking into the caller instead of being
gathered into one place. The obvious alternative -- putting these on the Storage
interface -- is not available: Storage is implemented by users for external
parameter servers, so adding methods to it would break them.

While collecting the capacity accessors, a second bound turned out to be
missing. meta["current_capacity"] is the key map's capacity, which is the
modulus for choosing a home bucket and so decides whether a slot means the same
thing in two tables. It is not what bounds a row write. The two are the same
number everywhere except NO_EVICTION, whose key map is deliberately
1 / max_load_factor times its value buffer -- and rounding that up to whole
buckets makes the map's capacity non-injective in the buffer's. With
bucket_capacity=128, an init_capacity of 100 and of 128 both give a 256-slot key
map, over 100 and 128 rows (measured, not derived). Those two tables passed the
compatibility check, and a source row of 127 was then written past the end of a
100-row target: device memory, not a wrong answer.

meta now also carries row_capacity, the value-buffer rows per tier, and
replay_increment compares it. Per tier rather than summed, since slot_index
routes each key to one tier and a matching total would say nothing about either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ro its block

The docstring opened with "a delta carries embeddings, not scores", which was
true when it was written and stopped being true once DeltaDumpResult grew a
scores column. A delta does carry the source's scores; this path runs when the
caller did not ask for ReplayContent.SCORE, so they were never loaded. Stating
the missing data as a property of the format rather than of the request reads
as a limitation instead of the choice it is, so say which flag turns it on.

Build the block with zeros rather than empty. The loop fills one column per
configured strategy while the width comes from the score policy; the two agree
for every strategy that exists, but they are derived from different places. A
divergence would leave columns holding whatever the allocator returned, written
into the table as scores -- silent and unreproducible. Zeroing costs nothing at
this size and makes that outcome deterministic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three of the score-related tests asserted something weaker or narrower than
their prose said.

test_dump_scores_are_column_aligned claimed to check logical column order while
configuring (TIMESTAMP, LFU) -- the compound policy's physical layout is always
that, so the permutation was the identity and the test passed with
score_dump_permutation deleted. Configure the reversed order instead, where the
frequency lands in column 0 only if the permutation runs. Its frequency check
was an ordering (hot outranks cold), which still holds if every count is off by
the same amount; assert the counts, one and five. Its timestamp column was not
checked at all, so add the one thing an age can be checked against without a
clock: the hot keys were touched last, so theirs is the smaller.

test_replay_scores_follow_the_content_flag inferred the replica's scores from
which keys a threshold selected. Read the scores instead. That makes the
ReplayContent.ALL case exact -- five and one, carried verbatim -- where before
it only had to split the same way, and lets the ReplayContent.EMBEDDING case say
what it means, that every restored key scores alike, rather than the roundabout
"all of them or none". The parameter is keeps_source_scores now: both cases have
scores, and which they have is the question.

test_replay_without_embedding_needs_aligned_rows is deleted. Replaying scores
without embeddings is not a case that arises, and the test only checked that the
guard fires, not that the embeddings it guards are preserved.

Also fixes two comments that said the hot keys were accessed six times. They are
accessed five.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jiashuy

jiashuy commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Pipeline #66690895 -- success

Job Status Log
pre_check ❌ failed view
train_build_x86 ✅ success view
train_build_arm64 ✅ success view
prepare-jet-b200-smoke ✅ success view
prepare-jet-b200-inference ✅ success view
prepare-jet-cw-dfw-e2e-benchmark ✅ success view
build_whl ✅ success view
dynamicemb_test_fwd_bwd_8gpus ✅ success view
dynamicemb_test_load_dump_8gpus ✅ success view
unit_test_1gpu_a100 ✅ success view
unit_test_1gpu_h100 ✅ success view
unit_test_4gpu ✅ success view
unit_test_tp_4gpu ❌ failed view
L20_unit_test_1gpu ✅ success view
inference_unit_test_1gpu ✅ success view
inference_test_1gpu ✅ success view
inference_test_nve_2605_1gpu ✅ success view

Result: 15/17 jobs passed

View full pipeline

…ntal_load

--incremental_dump trained a model and printed how many keys each dump matched.
It showed that incremental_dump returns something; it did not show what that
something is for.

It now writes each dump to its own file, and --incremental_load replays them
into a second model and reports its loss. Two commands over one --save_dir, so
the files are the whole handoff -- which is the shape delta replication has in
practice, a training job and a serving replica that never share a process.

The replica is built with training=False, so its value rows hold embeddings
alone. A delta from a training model still carries optimizer state; replay
writes what the target has room for and drops the rest.

Two things beyond the deltas turned out to be load-bearing.

The dense weights. Only embeddings travel in a delta, so without the trained MLP
the replica's loss is a fresh model's -- 17.1 against 2.0 here, which reads like
a broken replay and is not one. Embedding weights are not in state_dict, so
saving the model's state_dict is exactly the part a delta does not carry.

A dump after the training loop, not only the periodic ones. Training carries on
past the last periodic dump and those updates have to ship too; without it the
replica is out of date, and the forward disagrees for a reason that has nothing
to do with replay.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jiashuy
jiashuy force-pushed the feat/dynamicemb-replay-increment branch from c039ed7 to e6bb456 Compare September 8, 2026 06:11
Comment thread corelib/dynamicemb/example/example.py
jiashuy and others added 2 commits September 8, 2026 12:03
…ough

Two ways a replay could raise after it had already changed the table.

A key was installed at its slot by scatter_keys_at_slots, and only then checked
against same_key to see whether the row it landed on had been its own. Omitting
ReplayContent.EMBEDDING left nothing to write into that row, so a key that did
not already own it would go on serving the previous occupant's vector -- exactly
what the check existed to prevent, raised one step too late to prevent it.

The check is gone, and so is the option it guarded. There is no way to write a
key at a slot without deciding what its row holds, so the embedding always
travels with the key and ReplayContent chooses only what comes along:
OPTIMIZER_STATE, SCORE, or the empty EMBEDDING_ONLY. That removes the ordering
problem rather than paying an extra lookup to fix it, and it is honest about a
flag that could not be turned off safely anyway.

Compatibility was checked inside the per-table loop, so a delta spanning a
collection -- which is the normal shape -- could erase from and write to its
first tables and then raise on a later one's metadata. The docstring and
DynamicEmb_APIs both promise a mismatch raises "before anything is written";
that held for one table and not for several. Validate every table first, then
write.

The new test pins the second: two tables, the second incompatible, and the first
must come out untouched. Confirmed by re-interleaving validation with writes and
watching it fail -- a first attempt at that check was itself wrong, letting the
mismatched table raise in the validating pass so that nothing was written and
the test passed against the broken code.

Reported in review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… run

inc_dump kept the delta directory across runs while restarting its filename
counter at zero. A shorter run over a directory an earlier one had left behind
-- a dump that was interrupted, or produced and never loaded -- overwrote the
low numbers and left the high ones in place. inc_load replays every file it
matches, in order, so those stragglers landed last and wrote a previous run's
embeddings over the current one's.

Nothing downstream could catch it. Two runs of the same script share a config,
so the stale slot indices address the same layout: replay_increment's
compatibility check passes, the kernel's home-bucket check passes, and the
writes apply cleanly. They are simply the wrong values.

A dump run now owns its rank's sequence outright and removes the files matching
it before writing. Per rank rather than the whole directory, since each rank
already owns a disjoint set of filenames and clearing them separately needs no
coordination between ranks.

Verified by seeding delta_rank0_0003..0007 and running a dump that produces two
files: the directory comes out holding 0000 and 0001 alone, and the load that
follows reports the same loss as a run over a clean directory.

Reported in review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread corelib/dynamicemb/dynamicemb/batched_dynamicemb_tables.py Outdated
Comment thread corelib/dynamicemb/dynamicemb/dynamicemb_config.py
replay_increment had grown into one long loop that resolved, validated and
wrote each table in the same pass. Split it: _plan_replay resolves and checks
every table and writes nothing, _apply_replay writes one already-checked table.
"No table is written until every table has been checked" is now a property of
the control flow rather than something to be read out of a loop, and flush_cache
moves after planning so a rejected delta leaves the model exactly as it was.

The job carries everything its write needs -- erased_keys, evicted_keys and the
source's current_score -- so apply never reaches back into the delta and the
second subscript into the delta's column-aligned lists is gone.

Close one more way a delta could apply partway. Preflight validated only row
counts, so a wrong score width, a wrong optimizer-state width, a short
slot_index or a delta carrying no optimizer state at all (SGD dumped, rowwise
Adagrad replayed -- the layout check does not compare optimizers) all raised
down in the write path, by which time the collection's earlier tables were in.
Every shape the write path depends on is checked up front instead.

Docs: the design doc still said the delta carries embeddings only and never
optimizer state, and listed five tests that no longer exist while missing five
that do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jiashuy

jiashuy commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

/build

@JacoCheung

JacoCheung commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

❌ Pipeline #66736375 -- failed

Job Status Log
pre_check ❌ failed view
train_build_x86 ✅ success view
train_build_arm64 ✅ success view
prepare-jet-b200-smoke ✅ success view
prepare-jet-b200-inference ✅ success view
prepare-jet-cw-dfw-e2e-benchmark ✅ success view
build_whl ✅ success view
dynamicemb_test_fwd_bwd_8gpus ✅ success view
dynamicemb_test_load_dump_8gpus ✅ success view
unit_test_1gpu_a100 ✅ success view
unit_test_1gpu_h100 ✅ success view
unit_test_4gpu ✅ success view
unit_test_tp_4gpu ❌ failed view
L20_unit_test_1gpu ✅ success view
inference_unit_test_1gpu ❌ failed view
inference_test_1gpu ❌ failed view
inference_test_nve_2605_1gpu ❌ failed view

Result: 12/17 jobs passed

View full pipeline

Comment thread corelib/dynamicemb/docs/replay_increment_design.md
Comment thread corelib/dynamicemb/dynamicemb/key_value_table.py
@shijieliu
shijieliu merged commit 96e2b84 into NVIDIA:main Sep 10, 2026
1 check passed
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