Repository navigation
AISPARSE-692/693/694: gtsv offsets, zero-batch guard and BSR row-map bounds checks #12316
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
Merged
kliegeois
merged 14 commits into
ROCm:develop
from
kliegeois:aisparse-692-693-694-precond
Oct 13, 2026
Merged
Changes from 6 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7f685b5
fix(rocsparse): widen the gtsv interleaved batch row offsets to 64 bi…
kliegeois 39560a1
fix(rocsparse): guard zero batch counts and fix grid rounding overflo…
kliegeois ed0bfde
fix(rocsparse): bounds check the BSR row map and clamp grid.x (AISPAR…
kliegeois b623b87
Merge branch 'develop' into aisparse-692-693-694-precond
kliegeois cf72834
Merge branch 'develop' into aisparse-692-693-694-precond
kliegeois 9ea4e71
refactor(rocsparse): use rocsparse_grid.hpp helpers (AISPARSE-692-694)
kliegeois dd8cc6b
docs(rocsparse): correct spic0/spilu0 and csritilu0 CHANGELOG entries
kliegeois 864712f
refactor(rocsparse): move grid_x_chunk into rocsparse_grid.hpp (AISPA…
kliegeois 8399d24
test(rocsparse): run bsrilu0/bsric0 general chunk loop on clamped gri…
kliegeois ba88239
Merge branch 'develop' into aisparse-692-693-694-precond
kliegeois d2f439e
Merge remote-tracking branch 'upstream/develop' into HEAD
kliegeois 22e6dff
rocsparse: move the CHANGELOG entries to the unreleased 5.2.0 Resolve…
kliegeois df7a1d3
fix(rocsparse): clamp grid.x in the block-dim specific BSR ic0/ilu0 k…
kliegeois 7aaa1b0
test(rocsparse): run the block-dim specific BSR ic0/ilu0 chunk loops …
kliegeois 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
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
160 changes: 160 additions & 0 deletions
160
projects/rocsparse/clients/unittests/unit_test_internal_grid_x.cpp
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,160 @@ | ||
| /*! \file */ | ||
| /* ************************************************************************ | ||
| * Copyright (C) 2026 Advanced Micro Devices, Inc. All rights Reserved. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| * | ||
| * ************************************************************************ */ | ||
|
|
||
| // | ||
| // Host unit tests for the contiguous chunk partition that the incomplete | ||
| // factorizations use to recover the work above the grid.x clamp. | ||
| // | ||
| // These live in the host-only rocsparse-unit-test binary rather than in | ||
| // rocsparse-unit-test-device, and they declare no memory requirement, so | ||
| // nothing can drop them at instantiation time. The whole row count range the | ||
| // clamp exists for is far past what any available device can hold, so the | ||
| // properties the kernels depend on are checked here arithmetically instead: | ||
| // | ||
| // - the chunks are a partition: every item in [0, count) is owned by exactly | ||
| // one block; | ||
| // - the chunks are monotone in the block id, which is the property that keeps | ||
| // the done-flag spin in the factorization kernels from deadlocking; | ||
| // - at any unclamped extent the partition degenerates to one item per block, | ||
| // so the recovery loop is a no-op for every reachable problem size. | ||
| // | ||
| #include "rocsparse_grid_x.hpp" | ||
|
|
||
| #include <cstdint> | ||
| #include <gtest/gtest.h> | ||
| #include <vector> | ||
|
|
||
| TEST(grid_x, grid_x_chunk_is_a_partition) | ||
| { | ||
| for(int64_t count : {int64_t(1), | ||
| int64_t(2), | ||
| int64_t(5), | ||
| int64_t(16), | ||
| int64_t(97), | ||
| int64_t(1024), | ||
| int64_t(4097)}) | ||
| { | ||
| for(uint32_t grid_size = 1; grid_size <= static_cast<uint32_t>(count); ++grid_size) | ||
| { | ||
| std::vector<int> owners(static_cast<size_t>(count), 0); | ||
|
|
||
| for(uint32_t block_id = 0; block_id < grid_size; ++block_id) | ||
| { | ||
| int64_t first, last; | ||
| rocsparse::grid_x_chunk(count, grid_size, block_id, first, last); | ||
|
|
||
| ASSERT_LE(first, last) << "count " << count << " grid_size " << grid_size; | ||
|
|
||
| for(int64_t i = first; i < last; ++i) | ||
| { | ||
| ASSERT_GE(i, 0); | ||
| ASSERT_LT(i, count); | ||
| ++owners[static_cast<size_t>(i)]; | ||
| } | ||
| } | ||
|
|
||
| for(int64_t i = 0; i < count; ++i) | ||
| { | ||
| ASSERT_EQ(owners[static_cast<size_t>(i)], 1) | ||
| << "item " << i << " of " << count << " with grid_size " << grid_size; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(grid_x, grid_x_chunk_is_monotone_in_the_block_id) | ||
| { | ||
| // A block only ever waits on items belonging to itself or to a lower | ||
| // numbered block. A grid-stride loop would not have this property and would | ||
| // deadlock the done-flag spin once the grid exceeds what stays resident. | ||
| for(int64_t count : {int64_t(7), int64_t(100), int64_t(4097)}) | ||
| { | ||
| for(uint32_t grid_size = 1; grid_size <= static_cast<uint32_t>(count); ++grid_size) | ||
| { | ||
| int64_t previous_last = 0; | ||
|
|
||
| for(uint32_t block_id = 0; block_id < grid_size; ++block_id) | ||
| { | ||
| int64_t first, last; | ||
| rocsparse::grid_x_chunk(count, grid_size, block_id, first, last); | ||
|
|
||
| if(first < last) | ||
| { | ||
| ASSERT_GE(first, previous_last) | ||
| << "count " << count << " grid_size " << grid_size << " block " << block_id; | ||
| previous_last = last; | ||
| } | ||
| } | ||
|
|
||
| ASSERT_EQ(previous_last, count); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(grid_x, grid_x_chunk_degenerates_to_one_item_per_block_when_unclamped) | ||
| { | ||
| // Every problem size that fits on hardware today launches grid_size == | ||
| // count, and this is what makes the recovery loop a no-op there. | ||
| for(int64_t count : {int64_t(1), int64_t(3), int64_t(64), int64_t(65537), int64_t(100000)}) | ||
| { | ||
| const uint32_t grid_size = static_cast<uint32_t>(count); | ||
|
|
||
| for(uint32_t block_id = 0; block_id < grid_size; ++block_id) | ||
| { | ||
| int64_t first, last; | ||
| rocsparse::grid_x_chunk(count, grid_size, block_id, first, last); | ||
|
|
||
| ASSERT_EQ(first, static_cast<int64_t>(block_id)); | ||
| ASSERT_EQ(last, static_cast<int64_t>(block_id) + 1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TEST(grid_x, grid_x_chunk_covers_a_count_above_the_clamp) | ||
| { | ||
| // The case the clamp exists for: more items than the grid can express. | ||
| // grid_size is what rocsparse::get_grid_size_x returns at 256 threads per | ||
| // block, (2^32 - 1) / 256. Check the leading blocks and the final block | ||
| // without walking every item. | ||
| const int64_t count = 5000000000LL; | ||
| const uint32_t grid_size = 16777215u; | ||
|
|
||
| int64_t first, last; | ||
|
|
||
| rocsparse::grid_x_chunk(count, grid_size, 0, first, last); | ||
| EXPECT_EQ(first, 0); | ||
| EXPECT_EQ(last, 299); | ||
|
|
||
| int64_t total = 0; | ||
| for(uint32_t block_id = 0; block_id < 1000; ++block_id) | ||
| { | ||
| rocsparse::grid_x_chunk(count, grid_size, block_id, first, last); | ||
| total += last - first; | ||
| } | ||
| EXPECT_EQ(total, 299000); | ||
|
|
||
| rocsparse::grid_x_chunk(count, grid_size, grid_size - 1, first, last); | ||
| EXPECT_EQ(last, count); | ||
| EXPECT_LE(first, last); | ||
| } |
78 changes: 78 additions & 0 deletions
78
projects/rocsparse/library/src/include/rocsparse_grid_x.hpp
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,78 @@ | ||
| /*! \file */ | ||
| /* ************************************************************************ | ||
| * Copyright (C) 2026 Advanced Micro Devices, Inc. All rights Reserved. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| * | ||
| * ************************************************************************ */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| // | ||
| // Kept in its own header, and deliberately free of any rocsparse or HIP | ||
| // include, so that the host-only rocsparse-unit-test binary can compile it as | ||
| // plain C++ without pulling in the HIP toolchain. The launch clamps grid.x with | ||
| // rocsparse::get_grid_size_x (rocsparse_grid.hpp); grid_x_chunk recovers the | ||
| // work above the clamp inside the kernel. | ||
| // | ||
| #if defined(__HIPCC__) | ||
| #define ROCSPARSE_GRID_X_ILF static __host__ __device__ __forceinline__ | ||
| #else | ||
| #define ROCSPARSE_GRID_X_ILF static inline | ||
| #endif | ||
|
|
||
| namespace rocsparse | ||
| { | ||
| // | ||
| // Half-open range [first, last) of the count work items owned by block | ||
| // block_id when the items are split into grid_size contiguous chunks. The | ||
| // range is empty when the block has nothing to do, which happens for the | ||
| // trailing blocks when grid_size does not divide count. | ||
| // | ||
| // Contiguous chunks, not a grid-stride loop, and that is load bearing for | ||
| // the incomplete factorizations. Those kernels spin on the done flags of the | ||
| // rows they depend on, and the row map is a topological order, so an item | ||
| // only ever waits on items earlier in that order. With contiguous chunks | ||
| // every item a block waits on belongs either to itself, earlier in its own | ||
| // chunk, or to a lower numbered block, which the dispatcher started first. | ||
| // That is the same property the unclamped launch relies on today, and it is | ||
| // what keeps the spin from deadlocking when the grid is larger than the | ||
| // device can hold resident. | ||
| // | ||
| // A grid-stride loop does not have that property. Block 0 on its second item | ||
| // waits on the first item of block grid_size - 1, which cannot start until | ||
| // some block retires, and no block retires while it is spinning. | ||
| // | ||
| ROCSPARSE_GRID_X_ILF void grid_x_chunk( | ||
|
kliegeois marked this conversation as resolved.
Resolved
|
||
| int64_t count, uint32_t grid_size, uint32_t block_id, int64_t& first, int64_t& last) | ||
| { | ||
| const int64_t chunk = (count - 1) / static_cast<int64_t>(grid_size) + 1; | ||
| const int64_t start = static_cast<int64_t>(block_id) * chunk; | ||
|
|
||
| // | ||
| // Both ends are clamped, not just the upper one. grid_size does not have | ||
| // to divide count, so a trailing block can start past the end and must | ||
| // come back with an empty range rather than an inverted one. | ||
| // | ||
| first = (start < count) ? start : count; | ||
| last = ((first + chunk) < count) ? (first + chunk) : count; | ||
| } | ||
| } | ||
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
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.
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.