-
-
Notifications
You must be signed in to change notification settings - Fork 21k
buffer size insuffient Dspark sd for FlashInfer MNNVL allreduce #50932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
khushali9
wants to merge
12
commits into
vllm-project:main
Choose a base branch
from
khushali9:DSpark_bufferSizeinsuffient
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+33
−2
Open
Changes from 2 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
cf62018
buffer size insuffient Dspark sd for FlashInfer MNNVL allreduce
khushali9 f59effd
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 181da78
PR feedback addressed
khushali9 ea5a47c
comment removed
khushali9 ec9613a
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 b38dea2
updated fused_allreduce_gemma
khushali9 ffa5822
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 88a0ccd
PR feedback addressed
khushali9 4631216
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 8570ccd
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 0e93d92
Comment updated
khushali9 b030085
Merge branch 'main' into DSpark_bufferSizeinsuffient
khushali9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # SPDX-FileCopyrightText: Copyright contributors to the vLLM project | ||
| """Admission-gate tests for :class:`FlashInferAllReduce`. | ||
|
|
||
| These exercise the capacity accounting only, so they run on CPU without CUDA or | ||
| flashinfer installed: both the workspace and the input tensor are stubbed. | ||
| """ | ||
|
|
||
| import pytest | ||
| import torch | ||
|
|
||
| from vllm.distributed.device_communicators import flashinfer_all_reduce | ||
| from vllm.distributed.device_communicators.flashinfer_all_reduce import ( | ||
| FlashInferAllReduce, | ||
| ) | ||
| from vllm.utils.math_utils import round_up | ||
|
|
||
| # The repro from the bug report: DeepSeek-V4 hidden size, TP8, bf16, and the 2MB | ||
| # per-rank budget FI_ALLREDUCE_FUSION_MAX_SIZE_MB gives TP8 on sm103. | ||
| HIDDEN_DIM = 7168 | ||
| WORLD_SIZE = 8 | ||
| DTYPE = torch.bfloat16 | ||
| WORKSPACE_BUDGET = 2 * 1024 * 1024 | ||
|
|
||
| # Byte counts quoted in the traceback, reproduced by _FakeMnnvlWorkspace below. | ||
| REPORTED_BUFFER_BYTES = 698368 | ||
| REPORTED_REQUIRED_BYTES = 802816 | ||
|
|
||
| # Tokens whose bf16 payload exceeds a single Lamport buffer but still fits the | ||
| # whole workspace budget -- the window that used to reach the kernel and abort. | ||
| OVERSIZED_TOKENS = 56 | ||
| # Largest token count that genuinely fits one Lamport buffer here. | ||
| FITTING_TOKENS = 48 | ||
|
|
||
|
|
||
| class _FakeMnnvlWorkspace: | ||
| """Mimics flashinfer's MNNVL workspace capacity accounting. | ||
|
|
||
| flashinfer sizes one Lamport buffer at roughly ``payload / 3`` and allocates | ||
| three of them, so only about a third of the requested budget backs any single | ||
| all-reduce. ``is_buffer_size_sufficient`` is the real API that the production | ||
| code consults instead of reimplementing this arithmetic. | ||
| """ | ||
|
|
||
| NUM_LAMPORT_BUFFERS = 3 | ||
| backend = "mnnvl" | ||
|
|
||
| def __init__(self, max_token_num: int, hidden_dim: int, dtype: torch.dtype): | ||
| payload = max_token_num * hidden_dim * dtype.itemsize | ||
| self.buffer_size_bytes = round_up(payload // self.NUM_LAMPORT_BUFFERS, 1024) | ||
|
|
||
| def is_buffer_size_sufficient( | ||
| self, | ||
| tp_size: int, | ||
| num_tokens: int, | ||
| hidden_dim: int, | ||
| dtype: torch.dtype, | ||
| use_oneshot=None, | ||
| ) -> bool: | ||
| return num_tokens * hidden_dim * dtype.itemsize <= self.buffer_size_bytes | ||
|
|
||
|
|
||
| class _FakeTensor: | ||
| """Just the surface ``should_use_fi_ar`` inspects, so no GPU is needed.""" | ||
|
|
||
| def __init__(self, num_tokens: int, hidden_dim: int, dtype: torch.dtype): | ||
| self.shape = (num_tokens, hidden_dim) | ||
| self.dtype = dtype | ||
| self.is_cuda = True | ||
|
|
||
| def is_contiguous(self) -> bool: | ||
| return True | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def comm(monkeypatch): | ||
| """A FlashInferAllReduce wired to a stub MNNVL workspace. | ||
|
|
||
| ``__init__`` is bypassed because it needs a live process group and a CUDA | ||
| platform; the attributes it would set are filled in directly. The stub is | ||
| cached like the real process-wide singleton, so the first caller's shape | ||
| fixes the capacity for every later caller. | ||
| """ | ||
| singleton: list[_FakeMnnvlWorkspace] = [] | ||
|
|
||
| def fake_get_workspace(world_size, rank, max_token_num, hidden_dim, dtype, group): | ||
| if not singleton: | ||
| singleton.append(_FakeMnnvlWorkspace(max_token_num, hidden_dim, dtype)) | ||
| return singleton[0] | ||
|
|
||
| monkeypatch.setattr( | ||
| flashinfer_all_reduce, "get_fi_ar_workspace", fake_get_workspace | ||
| ) | ||
|
|
||
| obj = FlashInferAllReduce.__new__(FlashInferAllReduce) | ||
| obj.disabled = False | ||
| obj.group = None | ||
| obj.world_size = WORLD_SIZE | ||
| obj.rank = 0 | ||
| obj.device = "cuda:0" | ||
| obj.max_workspace_size = WORKSPACE_BUDGET | ||
| obj.max_num_tokens = 0 | ||
| obj.workspace = None | ||
| return obj | ||
|
|
||
|
|
||
| def test_rejects_tensor_larger_than_one_lamport_buffer(comm): | ||
| """The reported crash: 56 tokens fit the budget but not a Lamport buffer.""" | ||
| tensor = _FakeTensor(OVERSIZED_TOKENS, HIDDEN_DIM, DTYPE) | ||
|
|
||
| assert comm.should_use_fi_ar(tensor) is False | ||
|
|
||
| # The cheap budget bound admits it, so only the authoritative workspace check | ||
| # can be what rejected it. Without that check this tensor reached the kernel. | ||
| assert comm.max_num_tokens >= OVERSIZED_TOKENS | ||
|
|
||
|
|
||
| def test_accepts_tensor_that_fits_one_lamport_buffer(comm): | ||
| tensor = _FakeTensor(FITTING_TOKENS, HIDDEN_DIM, DTYPE) | ||
|
|
||
| assert comm.should_use_fi_ar(tensor) is True | ||
| assert comm.workspace is not None | ||
|
|
||
|
|
||
| def test_reproduces_the_byte_counts_from_the_traceback(comm): | ||
| oversized = _FakeTensor(OVERSIZED_TOKENS, HIDDEN_DIM, DTYPE) | ||
| assert comm.should_use_fi_ar(oversized) is False | ||
|
|
||
| # "Buffer: 698368 bytes, Required: 802816 bytes." | ||
| assert comm.workspace.buffer_size_bytes == REPORTED_BUFFER_BYTES | ||
| required = OVERSIZED_TOKENS * HIDDEN_DIM * DTYPE.itemsize | ||
| assert required == REPORTED_REQUIRED_BYTES | ||
|
|
||
|
|
||
| def test_still_rejects_tensors_beyond_the_whole_budget(comm): | ||
| """The cheap pre-check must keep short-circuiting huge prefill tensors.""" | ||
| huge = _FakeTensor(8192, HIDDEN_DIM, DTYPE) | ||
|
|
||
| assert comm.should_use_fi_ar(huge) is False | ||
| # Rejected before any workspace was created. | ||
| assert comm.workspace is None | ||
|
|
||
|
|
||
| def test_gate_follows_payload_not_the_first_shape_seen(comm): | ||
| """A drafter with a different hidden size must not inherit a stale limit.""" | ||
| assert comm.should_use_fi_ar(_FakeTensor(FITTING_TOKENS, HIDDEN_DIM, DTYPE)) is True | ||
|
|
||
| # Same token count, twice the hidden size: twice the payload, so it no longer | ||
| # fits, even though the cached token-count bound would still admit it. | ||
| wide = _FakeTensor(FITTING_TOKENS, HIDDEN_DIM * 2, DTYPE) | ||
| assert comm.max_num_tokens >= FITTING_TOKENS | ||
| assert comm.should_use_fi_ar(wide) is False | ||
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
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.