-
Notifications
You must be signed in to change notification settings - Fork 257
perf(cuda): cap SM86 Stream-K grid to useful chunks #583
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
Amidwestnoob
wants to merge
2
commits into
Luce-Org:main
Choose a base branch
from
Amidwestnoob:exp/sm86-streamk-pr
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.
+130
−4
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
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
51 changes: 51 additions & 0 deletions
51
server/deps/llama.cpp/ggml/src/ggml-cuda/mmq-streamk-schedule.h
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,51 @@ | ||
| // Pure host-side MMQ stream-k grid selection (no CUDA types). | ||
| // Shared by mmq.cuh and host unit tests. | ||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
|
|
||
| static inline int mmq_stream_k_nblocks( | ||
| const int ntiles_dst, | ||
| const int nsm, | ||
| const int64_t ncols_x, | ||
| const int iter_k, | ||
| const bool is_nvidia, | ||
| const bool enable_useful_chunk_cap) { | ||
| if (ntiles_dst <= 0 || nsm <= 0 || ncols_x <= 0 || iter_k <= 0) { | ||
| return 1; | ||
| } | ||
|
|
||
| const int64_t tiles_nwaves = (static_cast<int64_t>(ntiles_dst) + nsm - 1) / nsm; | ||
| const int64_t tiles_efficiency_percent = | ||
| 100 * static_cast<int64_t>(ntiles_dst) / (static_cast<int64_t>(nsm) * tiles_nwaves); | ||
|
|
||
| // Preserve the pre-existing NVIDIA >=90% pure-tiling escape. | ||
| if (is_nvidia && tiles_efficiency_percent >= 90) { | ||
| return ntiles_dst; | ||
| } | ||
|
|
||
| // Fail closed outside the specifically validated hardware path. | ||
| if (!enable_useful_chunk_cap) { | ||
| return nsm; | ||
| } | ||
|
|
||
| // The device aligns CTA boundaries down to complete iter_k/qk chunks. | ||
| // Keep a partial K tail with the tile's final CTA instead of counting it | ||
| // as another CTA; otherwise the aligned partition creates empty CTAs. | ||
| const int64_t iters_per_tile = ncols_x >= iter_k ? ncols_x / iter_k : 1; | ||
| // We only need the exact product when it is below nsm. Saturate first so | ||
| // neither division nor multiplication can overflow. | ||
| if (iters_per_tile >= nsm) { | ||
| return nsm; | ||
| } | ||
| const int64_t tiles_needed_to_fill = (nsm + iters_per_tile - 1) / iters_per_tile; | ||
| if (static_cast<int64_t>(ntiles_dst) >= tiles_needed_to_fill) { | ||
| return nsm; | ||
| } | ||
| const int64_t total_iters = static_cast<int64_t>(ntiles_dst) * iters_per_tile; | ||
| return total_iters > 0 ? static_cast<int>(total_iters) : 1; | ||
| } | ||
|
|
||
| static inline bool mmq_stream_k_fixup_needed(const int ntiles_dst, const int stream_k_blocks) { | ||
| return stream_k_blocks > 0 && ntiles_dst % stream_k_blocks != 0; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #include "mmq-streamk-schedule.h" | ||
|
|
||
| #include <climits> | ||
| #include <cstdint> | ||
| #include <cstdio> | ||
|
|
||
| namespace { | ||
| constexpr int kIterK = 256; | ||
| constexpr int kNsm86 = 82; | ||
| int failures = 0; | ||
|
|
||
| void expect(const char * name, int actual, int wanted) { | ||
| const bool pass = actual == wanted; | ||
| std::printf("[%s] %-34s actual=%d expected=%d\n", pass ? "PASS" : "FAIL", name, actual, wanted); | ||
| failures += pass ? 0 : 1; | ||
| } | ||
| } // namespace | ||
|
|
||
| int main() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reference other unittest. use the exist test framework and avoid creating a new executable for test |
||
| // Validated SM86 path: avoid empty CTAs for shallow K. | ||
| expect("sm86 shallow pure tiling", mmq_stream_k_nblocks(20, 82, 256, kIterK, true, true), 20); | ||
| expect("sm86 partial fill", mmq_stream_k_nblocks(20, 82, 512, kIterK, true, true), 40); | ||
| expect("sm86 deep K unchanged", mmq_stream_k_nblocks(20, 82, 5120, kIterK, true, true), 82); | ||
| expect("sm86 single shallow", mmq_stream_k_nblocks(1, 82, 256, kIterK, true, true), 1); | ||
| expect("sm86 single deep capped", mmq_stream_k_nblocks(1, 82, 5120, kIterK, true, true), 20); | ||
|
|
||
| // qk-aligned K tails stay with the tile's final CTA. Counting ceil(K/256) | ||
| // would produce alternating empty CTAs and a spurious fixup launch. | ||
| const int k320_stream_blocks = mmq_stream_k_nblocks(20, 82, 320, kIterK, true, true); | ||
| expect("sm86 K320 qk32 tail", k320_stream_blocks, 20); | ||
| expect("sm86 K320 skips fixup", mmq_stream_k_fixup_needed(20, k320_stream_blocks), 0); | ||
| expect("sm86 K384 qk32 tail", mmq_stream_k_nblocks(20, 82, 384, kIterK, true, true), 20); | ||
| const int k576_stream_blocks = mmq_stream_k_nblocks(20, 82, 576, kIterK, true, true); | ||
| expect("sm86 K576 split remains", k576_stream_blocks, 40); | ||
| expect("sm86 K576 needs fixup", mmq_stream_k_fixup_needed(20, k576_stream_blocks), 1); | ||
|
|
||
| // Existing >=90% NVIDIA tiling behavior remains unchanged. | ||
| expect("nvidia 90pct tiling", mmq_stream_k_nblocks(74, 82, 5120, kIterK, true, false), 74); | ||
| expect("nvidia full SM tiling", mmq_stream_k_nblocks(82, 82, 5120, kIterK, true, false), 82); | ||
|
|
||
| // Fail closed: non-SM86 and non-NVIDIA paths retain the old nsm schedule. | ||
| expect("other nvidia unchanged", mmq_stream_k_nblocks(20, 82, 256, kIterK, true, false), 82); | ||
| expect("non-nvidia unchanged", mmq_stream_k_nblocks(20, 82, 256, kIterK, false, false), 82); | ||
|
|
||
| // Invalid inputs remain bounded, and 64-bit arithmetic avoids overflow. | ||
| expect("invalid ntiles", mmq_stream_k_nblocks(0, 82, 256, kIterK, true, true), 1); | ||
| expect("invalid nsm", mmq_stream_k_nblocks(20, 0, 256, kIterK, true, true), 1); | ||
| expect("invalid K", mmq_stream_k_nblocks(20, 82, 0, kIterK, true, true), 1); | ||
| expect("invalid iter", mmq_stream_k_nblocks(20, 82, 256, 0, true, true), 1); | ||
| expect("large shape no overflow", mmq_stream_k_nblocks(INT_MAX / 2, 82, INT64_MAX / 4, kIterK, true, true), INT_MAX / 2); | ||
|
|
||
| // Fixup predicate itself remains unchanged. | ||
| expect("tiling skips fixup", mmq_stream_k_fixup_needed(20, 20), 0); | ||
| expect("partial needs fixup", mmq_stream_k_fixup_needed(20, 40), 1); | ||
| expect("deep needs fixup", mmq_stream_k_fixup_needed(20, 82), 1); | ||
|
|
||
| if (failures) { | ||
| std::fprintf(stderr, "FAILED: %d schedule checks\n", failures); | ||
| return 1; | ||
| } | ||
| std::puts("ALL PASS: SM86 Stream-K schedule checks"); | ||
| return 0; | ||
| } | ||
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const bool enable_useful_chunk_cap = (cc == 860);
make it explict