Files
TheRock/rocm-systems/projects/hip-tests/catch/unit/executionContext/hipExecutionCtxResourceSplit.cc
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

401 lines
13 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
/**
* @addtogroup hipExecutionCtxResourceSplit hipExecutionCtxResourceSplit
* @{
* @ingroup ExecutionContextTest
* `hipDevSmResourceSplit` and `hipDevSmResourceSplitByCount` APIs
*/
#include <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include "hip_executionctx_common.hh"
#include <algorithm>
#include <vector>
/**
* Test Description
* ------------------------
* - Splits device SM resources into a single group containing half the total
* SMs (aligned to smCoscheduledAlignment) via hipDevSmResourceSplit.
* Verifies the result SM count and that the remainder captures the rest.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_Sanity) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int totalSMs = input.sm.smCount;
unsigned int alignment = input.sm.smCoscheduledAlignment;
unsigned int halfSMs = (totalSMs / 2 / alignment) * alignment;
REQUIRE(halfSMs >= alignment);
hipDevSmResourceGroupParams params[1] = {};
params[0].smCount = halfSMs;
hipDevResource result[1] = {};
hipDevResource remainder{};
HIP_CHECK(hipDevSmResourceSplit(result, 1, &input, &remainder, 0, params));
REQUIRE(result[0].type == hipDevResourceTypeSm);
REQUIRE(result[0].sm.smCount == halfSMs);
REQUIRE(remainder.type == hipDevResourceTypeSm);
REQUIRE(remainder.sm.smCount == totalSMs - halfSMs);
}
/**
* Test Description
* ------------------------
* - Splits device SM resources via hipDevSmResourceSplitByCount using discovery
* mode (result=NULL) to query possible groups, then performs the actual split
* and verifies group counts and SM totals.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_By_Count_Sanity) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int nbGroups = 0;
HIP_CHECK(hipDevSmResourceSplitByCount(nullptr, &nbGroups, &input, nullptr, 0, 2));
REQUIRE(nbGroups > 0);
std::vector<hipDevResource> result(nbGroups);
hipDevResource remainder{};
unsigned int actualGroups = nbGroups;
HIP_CHECK(hipDevSmResourceSplitByCount(result.data(), &actualGroups, &input, &remainder, 0, 2));
REQUIRE(actualGroups == nbGroups);
unsigned int totalAssigned = 0;
for (unsigned int i = 0; i < actualGroups; i++) {
REQUIRE(result[i].type == hipDevResourceTypeSm);
REQUIRE(result[i].sm.smCount >= 2);
totalAssigned += result[i].sm.smCount;
}
unsigned int remainderSMs = 0;
if (remainder.type == hipDevResourceTypeSm) {
remainderSMs = remainder.sm.smCount;
}
REQUIRE(totalAssigned + remainderSMs == input.sm.smCount);
}
/**
* Test Description
* ------------------------
* - Validates error codes from hipDevSmResourceSplit for invalid parameters:
* NULL input, NULL groupParams, non-zero flags, wrong resource type,
* smCount < 2, and smCount exceeding total SMs.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_Negative) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
hipDevResource result[2] = {};
hipDevSmResourceGroupParams params[2] = {};
params[0].smCount = 2;
params[1].smCount = 2;
// NULL input
REQUIRE(hipDevSmResourceSplit(result, 2, nullptr, nullptr, 0, params)
== hipErrorInvalidValue);
// NULL groupParams
REQUIRE(hipDevSmResourceSplit(result, 2, &input, nullptr, 0, nullptr)
== hipErrorInvalidValue);
// Non-zero flags (currently unsupported)
hipDevSmResourceGroupParams validParams[1] = {};
validParams[0].smCount = 2;
REQUIRE(hipDevSmResourceSplit(result, 1, &input, nullptr, 1, validParams)
== hipErrorInvalidValue);
// Wrong resource type
hipDevResource badInput = input;
badInput.type = hipDevResourceTypeInvalid;
REQUIRE(hipDevSmResourceSplit(result, 1, &badInput, nullptr, 0, validParams)
== hipErrorInvalidResourceType);
// smCount < minSmPartitionSize (must be >= minSmPartitionSize)
if (input.sm.minSmPartitionSize > 1) {
hipDevSmResourceGroupParams tooSmall[1] = {};
tooSmall[0].smCount = input.sm.minSmPartitionSize - 1;
REQUIRE(hipDevSmResourceSplit(result, 1, &input, nullptr, 0, tooSmall)
== hipErrorInvalidResourceConfiguration);
}
// smCount exceeds total SMs
hipDevSmResourceGroupParams tooLarge[1] = {};
tooLarge[0].smCount = input.sm.smCount + 2;
REQUIRE(hipDevSmResourceSplit(result, 1, &input, nullptr, 0, tooLarge)
== hipErrorInvalidResourceConfiguration);
// Total smCount across groups exceeds available SMs
hipDevSmResourceGroupParams overcommit[2] = {};
overcommit[0].smCount = input.sm.smCount;
overcommit[1].smCount = 2;
REQUIRE(hipDevSmResourceSplit(result, 2, &input, nullptr, 0, overcommit)
== hipErrorInvalidResourceConfiguration);
}
/**
* Test Description
* ------------------------
* - Uses hipDevSmResourceSplitByCount discovery to find the supported number
* of groups. If at least 3 groups are possible, performs an asymmetric
* hipDevSmResourceSplit (1x and 2x alignment); otherwise falls back to a
* symmetric split (alignment + alignment). Creates a execution context and
* stream from each partition, runs a vectorADD kernel, and verifies
* correctness.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_Functional) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int totalSMs = input.sm.smCount;
unsigned int alignment = input.sm.smCoscheduledAlignment;
unsigned int nbGroups = 0;
HIP_CHECK(hipDevSmResourceSplitByCount(nullptr, &nbGroups, &input, nullptr, 0, alignment));
REQUIRE(nbGroups >= 2);
unsigned int groupA = alignment;
unsigned int groupB = (nbGroups >= 3) ? alignment * 2 : alignment;
hipDevSmResourceGroupParams params[2] = {};
params[0].smCount = groupA;
params[1].smCount = groupB;
hipDevResource result[2] = {};
hipDevResource remainder{};
HIP_CHECK(hipDevSmResourceSplit(result, 2, &input, &remainder, 0, params));
REQUIRE(result[0].type == hipDevResourceTypeSm);
REQUIRE(result[0].sm.smCount == groupA);
REQUIRE(result[1].type == hipDevResourceTypeSm);
REQUIRE(result[1].sm.smCount == groupB);
unsigned int remainderSMs = 0;
if (remainder.type == hipDevResourceTypeSm) {
remainderSMs = remainder.sm.smCount;
}
REQUIRE(result[0].sm.smCount + result[1].sm.smCount + remainderSMs == totalSMs);
RunVectorAddOnResource(&result[0], 0);
RunVectorAddOnResource(&result[1], 0);
}
/**
* Test Description
* ------------------------
* - Validates error codes from hipDevSmResourceSplitByCount for invalid
* parameters: NULL nbGroups, NULL input, and wrong resource type.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_By_Count_Negative) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int nbGroups = 2;
// NULL nbGroups
REQUIRE(hipDevSmResourceSplitByCount(nullptr, nullptr, &input, nullptr, 0, 2)
== hipErrorInvalidValue);
// NULL input
REQUIRE(hipDevSmResourceSplitByCount(nullptr, &nbGroups, nullptr, nullptr, 0, 2)
== hipErrorInvalidValue);
// Wrong resource type
hipDevResource badInput = input;
badInput.type = hipDevResourceTypeInvalid;
REQUIRE(hipDevSmResourceSplitByCount(nullptr, &nbGroups, &badInput, nullptr, 0, 2)
== hipErrorInvalidResourceType);
}
/**
* Test Description
* ------------------------
* - Splits SM resources via hipDevSmResourceSplitByCount into 2 partitions
* using a minCount of ~40% of total SMs. Any SMs left over due to
* alignment are captured in the remainder. Creates a execution context and
* stream from each partition, runs a vectorADD kernel, and verifies
* correctness.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_By_Count_Functional) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int totalSMs = input.sm.smCount;
unsigned int alignment = input.sm.smCoscheduledAlignment;
unsigned int minCount = min(alignment, (static_cast<unsigned int>(totalSMs * 0.4) / alignment) * alignment);
unsigned int nbGroups = 0;
HIP_CHECK(hipDevSmResourceSplitByCount(nullptr, &nbGroups, &input, nullptr, 0, minCount));
REQUIRE(nbGroups >= 2);
unsigned int requestedGroups = 2;
hipDevResource result[2] = {};
hipDevResource remainder{};
HIP_CHECK(hipDevSmResourceSplitByCount(result, &requestedGroups, &input, &remainder, 0,
minCount));
REQUIRE(requestedGroups == 2);
unsigned int assignedSMs = result[0].sm.smCount + result[1].sm.smCount;
unsigned int remainderSMs = 0;
if (remainder.type == hipDevResourceTypeSm) {
remainderSMs = remainder.sm.smCount;
}
REQUIRE(assignedSMs + remainderSMs == totalSMs);
RunVectorAddOnResource(&result[0], 0);
RunVectorAddOnResource(&result[1], 0);
}
/**
* Test Description
* ------------------------
* - Uses hipDevSmResourceSplitByCount discovery to find the supported number
* of groups, then splits SM resources into 2 groups via hipDevSmResourceSplit
* where the first group gets ~65% of SMs (capped to the discoverable range)
* and the second group uses the backfill flag to absorb all remaining SMs.
* Verifies that the remainder is invalid and runs a vectorADD kernel on each
* partition.
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_Backfill_Functional) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int totalSMs = input.sm.smCount;
unsigned int alignment = input.sm.smCoscheduledAlignment;
unsigned int nbGroups = 0;
HIP_CHECK(hipDevSmResourceSplitByCount(nullptr, &nbGroups, &input, nullptr, 0, alignment));
REQUIRE(nbGroups >= 2);
unsigned int maxExplicit = (nbGroups - 1) * alignment;
unsigned int group0 = (static_cast<unsigned int>(totalSMs * 0.65) / alignment) * alignment;
group0 = std::min(group0, maxExplicit);
REQUIRE(group0 >= alignment);
hipDevSmResourceGroupParams params[2] = {};
params[0].smCount = group0;
params[1].smCount = 0;
params[1].flags = hipDevSmResourceGroupBackfill;
hipDevResource result[2] = {};
hipDevResource remainder{};
HIP_CHECK(hipDevSmResourceSplit(result, 2, &input, &remainder, 0, params));
REQUIRE(result[0].sm.smCount == group0);
REQUIRE(result[1].sm.smCount == totalSMs - group0);
REQUIRE(remainder.type == hipDevResourceTypeInvalid);
RunVectorAddOnResource(&result[0], 0);
RunVectorAddOnResource(&result[1], 0);
}
#if HT_AMD
/**
* Test Description
* ------------------------
* - Splits SM resources into 3 equal groups via hipDevSmResourceSplit, creates
* a execution context and stream from each partition, retrieves CU masks via
* hipExtStreamGetCUMask, and verifies that every pair of masks is disjoint
* (bitwise AND is zero).
*/
HIP_TEST_CASE(Unit_hipExecutionCtxResourceSplit_Disjoint_Sets) {
HIP_CHECK(hipSetDevice(0));
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, 0));
hipDevResource input{};
HIP_CHECK(hipDeviceGetDevResource(device, &input, hipDevResourceTypeSm));
unsigned int totalSMs = input.sm.smCount;
unsigned int alignment = input.sm.smCoscheduledAlignment;
unsigned int groupSize = (totalSMs / 3 / alignment) * alignment;
if (groupSize < alignment) {
HIP_SKIP_TEST(HipTest::SkipReason::kSmCountTooSmall);
}
hipDevSmResourceGroupParams params[3] = {};
params[0].smCount = groupSize;
params[1].smCount = groupSize;
params[2].smCount = groupSize;
hipDevResource result[3] = {};
hipDevResource remainder{};
HIP_CHECK(hipDevSmResourceSplit(result, 3, &input, &remainder, 0, params));
for (int i = 0; i < 3; i++) {
REQUIRE(result[i].type == hipDevResourceTypeSm);
REQUIRE(result[i].sm.smCount == groupSize);
}
unsigned int totalWGPs = (alignment > 1) ? totalSMs / alignment : totalSMs;
unsigned int maskWords = (totalWGPs + 31) / 32;
std::vector<std::vector<uint32_t>> masks(3);
hipExecutionCtx_t ctx[3] = {};
hipStream_t stream[3] = {};
for (int i = 0; i < 3; i++) {
hipDevResourceDesc_t desc{};
HIP_CHECK(hipDevResourceGenerateDesc(&desc, &result[i], 1));
HIP_CHECK(hipGreenCtxCreate(&ctx[i], desc, 0, 0));
REQUIRE(ctx[i] != nullptr);
HIP_CHECK(hipExecutionCtxStreamCreate(&stream[i], ctx[i], hipStreamNonBlocking, 0));
REQUIRE(stream[i] != nullptr);
masks[i].resize(maskWords, 0);
HIP_CHECK(hipExtStreamGetCUMask(stream[i], maskWords, masks[i].data()));
}
for (int i = 0; i < 3; i++) {
for (int j = i + 1; j < 3; j++) {
for (unsigned int w = 0; w < maskWords; w++) {
REQUIRE((masks[i][w] & masks[j][w]) == 0);
}
}
}
for (int i = 0; i < 3; i++) {
HIP_CHECK(hipStreamDestroy(stream[i]));
HIP_CHECK(hipExecutionCtxDestroy(ctx[i]));
}
}
#endif
/**
* End doxygen group hipExecutionCtxResourceSplit.
* @}
*/