Multi node data generation offline#526
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR adds multi-node dataset partitioning support for offline hidden-state generation. A new helper module extracts index discovery and rank-based partitioning logic, the main script integrates these helpers via new ChangesMulti-Node Offline Data Generation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The quality checks have failed. Please run |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
tests/unit/data_generation/test_offline.py (1)
12-77: ⚡ Quick winAdd edge-case tests for
max_samples=0and out-of-rangeexistingindices.Current suite misses two high-value boundaries that affect correctness of the new partitioning path.
Suggested additions
class TestGetIndicesToProcess: + def test_max_samples_zero_returns_empty(self): + result = get_indices_to_process(10, 0, [], world_size=1, rank=0) + assert result == [] + + def test_existing_out_of_range_does_not_mark_all_processed(self): + result = get_indices_to_process(3, None, [10, 11, 12], world_size=1, rank=0) + assert result == [0, 1, 2]As per coding guidelines, "Ensure PyTest tests are clear, comprehensive, and cover edge cases specific to speculative decoding ... Verify that new code paths introduced in the PR are covered."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/data_generation/test_offline.py` around lines 12 - 77, Add two pytest cases to cover max_samples=0 and out-of-range existing indices for get_indices_to_process: (1) test that when max_samples=0 the function returns an empty list for any world_size/rank (e.g., call get_indices_to_process(10, 0, [], world_size=1, rank=0) and assert []), and (2) test that out-of-range values in the existing list are ignored (e.g., pass existing containing negative and >=num_samples values like [-1, 10, 999] to get_indices_to_process(5, None, existing, world_size=1, rank=0) and assert it returns list(range(5))); add these to the TestGetIndicesToProcess class so the new partitioning path is exercised and boundaries validated.docs/user_guide/tutorials/train_eagle3_offline.md (1)
157-171: ⚡ Quick winClarify shared storage requirement for multi-node generation.
The documentation mentions "shared (or later merged) output directory" but doesn't explain the implications of non-shared storage or how to merge outputs afterward. If nodes don't share the output directory during generation, each node will see different existing files and the partitioning logic may produce overlapping or incorrect work distribution.
Consider adding a note that clarifies:
- Whether shared storage (e.g., NFS) is required during generation, or
- If separate storage is used, how outputs should be merged and whether re-running with merged directories is necessary
📚 Suggested clarification
**Use multiple nodes:** -If you have access to multiple machines, each with its own vLLM server, you can split the dataset across them with `--world-size` and `--rank`. Each node generates a contiguous, non-overlapping chunk of the data into a shared (or later merged) output directory. See the [data_generation_offline.py cli reference](/cli/data_generation_offline.md) for details. +If you have access to multiple machines, each with its own vLLM server, you can split the dataset across them with `--world-size` and `--rank`. Each node generates a contiguous, non-overlapping chunk of the data. + +**Important:** All nodes should write to a shared output directory (e.g., mounted via NFS) so that each node can detect already-completed samples and resume correctly if interrupted. If using separate local storage per node, you must manually merge the output directories after generation completes. + +See the [data_generation_offline.py cli reference](/cli/data_generation_offline.md) for details.As per coding guidelines, ensure documentation is clear and complete.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/user_guide/tutorials/train_eagle3_offline.md` around lines 157 - 171, Update the docs/user_guide/tutorials/train_eagle3_offline.md section that describes multi-node generation to explicitly state the shared-storage requirement and the correct workflow when nodes do not share storage: clarify that if the nodes have a shared output directory (e.g., NFS) the partitioning via --world-size and --rank will produce non-overlapping chunks safely, otherwise each node will write separate outputs that must be merged later; describe a recommended merge procedure (concatenate or deduplicate per-sample files into a single output directory) and note that after merging you should treat the merged directory as the canonical preprocessed-data for any downstream run (or re-run data_generation_offline.py with consistent --world-size/--rank if necessary). Reference the data_generation_offline.py script and the --world-size/--rank flags in the note so readers know where partitioning behavior is controlled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/data_generation_offline.py`:
- Around line 170-187: Add validation after parsing arguments in parse_args() to
ensure the --rank value is within [0, world-size): check that args.rank is >= 0
and args.rank < args.world_size and raise parser.error (or SystemExit with a
clear message) if not; update any callers (e.g., main()) to use the validated
args returned from parse_args() so invalid rank values are caught early and
produce a clear error message referencing the offending values.
- Around line 170-187: The help strings for parser.add_argument entries for
"--world-size" and "--rank" concatenate two adjacent string literals without a
space after the colon, producing "IMPORTANT: thisis..."; update the help text
for the parser.add_argument calls for "--world-size" and "--rank" (the help
tuples) by inserting a space after the colon so the fragments become "IMPORTANT:
this is ..." (you can add the trailing space at the end of the first literal or
a leading space at the second literal).
In `@src/speculators/data_generation/offline.py`:
- Around line 55-60: Validate inputs before computing chunk_size: check that
world_size is an integer > 0 and that rank is an integer in the range 0 <= rank
< world_size; if not, raise a ValueError with a clear message. Add these checks
immediately before the chunk_size calculation (the block using variables
world_size, rank, chunk_size, remainder, start, end) so you avoid
ZeroDivisionError and silent invalid slices and fail fast with informative
errors.
- Around line 49-53: The early-return and target calculation are wrong: treat
max_samples<=0 as "no limit", compute target before checking completion, and
normalize/filter existing entries to valid, deduplicated sample indices;
specifically, normalize max_samples to None if <=0, compute target =
min(max_samples, num_samples) only when max_samples is set else target =
num_samples, replace existing with a filtered set containing only indices 0 <= i
< num_samples to remove stale/out-of-range entries, then perform the completion
check using len(existing_set) >= target and return []; update the block around
variables existing, num_samples, max_samples and target so the check uses the
filtered set and correct target.
---
Nitpick comments:
In `@docs/user_guide/tutorials/train_eagle3_offline.md`:
- Around line 157-171: Update the
docs/user_guide/tutorials/train_eagle3_offline.md section that describes
multi-node generation to explicitly state the shared-storage requirement and the
correct workflow when nodes do not share storage: clarify that if the nodes have
a shared output directory (e.g., NFS) the partitioning via --world-size and
--rank will produce non-overlapping chunks safely, otherwise each node will
write separate outputs that must be merged later; describe a recommended merge
procedure (concatenate or deduplicate per-sample files into a single output
directory) and note that after merging you should treat the merged directory as
the canonical preprocessed-data for any downstream run (or re-run
data_generation_offline.py with consistent --world-size/--rank if necessary).
Reference the data_generation_offline.py script and the --world-size/--rank
flags in the note so readers know where partitioning behavior is controlled.
In `@tests/unit/data_generation/test_offline.py`:
- Around line 12-77: Add two pytest cases to cover max_samples=0 and
out-of-range existing indices for get_indices_to_process: (1) test that when
max_samples=0 the function returns an empty list for any world_size/rank (e.g.,
call get_indices_to_process(10, 0, [], world_size=1, rank=0) and assert []), and
(2) test that out-of-range values in the existing list are ignored (e.g., pass
existing containing negative and >=num_samples values like [-1, 10, 999] to
get_indices_to_process(5, None, existing, world_size=1, rank=0) and assert it
returns list(range(5))); add these to the TestGetIndicesToProcess class so the
new partitioning path is exercised and boundaries validated.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9246024a-996d-4a09-a5da-475b75f20cf4
📒 Files selected for processing (6)
docs/cli/data_generation_offline.mddocs/user_guide/tutorials/train_eagle3_offline.mdscripts/data_generation_offline.pysrc/speculators/data_generation/offline.pytests/unit/data_generation/__init__.pytests/unit/data_generation/test_offline.py
6115c89 to
293baad
Compare
47fb431 to
e6d53b7
Compare
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
e6d53b7 to
f9c870a
Compare
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
PLEASE FILL IN THE PR DESCRIPTION HERE ENSURING ALL CHECKLIST ITEMS (AT THE BOTTOM) HAVE BEEN CONSIDERED.
Purpose
To speed up offline hidden states generation, this pr adds support for partitioning the data generation process across multiple nodes.
Description
This pr adds
--world-sizeand--rankargs to the script. When set, they will partition the dataset so the firstN//k(or plus 1) samples get processed by the first node, the second batch by the second node, etc, whereNis the total dataset size andkis the world size.The script should handle partitioning when
Nis not divisible bykby increasing the size of the firstN % knodes batches by 1.The changes shouldn't interfere with the ability to resume data generation that has been stopped.
Related Issue
Tests
Added tests for the partitioning / finding existing samples logic.
I have filled in: