Files
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

315 lines
11 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <catch2/catch_test_macros.hpp>
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Transforms/All.hpp>
#include <rocRoller/Scheduling/LDSModel.hpp>
#include <common/CommonGraphs.hpp>
#include <common/Scheduling.hpp>
#include "CustomSections.hpp"
#include "TestContext.hpp"
namespace LDSModelTest
{
using namespace rocRoller;
using namespace rocRoller::Scheduling::LDSModel;
TEST_CASE("LDS model get threads per clock", "[lds-bank-model]")
{
SECTION("GFX950 read operations")
{
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
CHECK(getThreadsPerClock(ldsRead, 1, GPUArchitectureGFX::GFX950) == 16);
CHECK(getThreadsPerClock(ldsRead, 2, GPUArchitectureGFX::GFX950) == 16);
CHECK(getThreadsPerClock(ldsRead, 3, GPUArchitectureGFX::GFX950) == 4);
CHECK(getThreadsPerClock(ldsRead, 4, GPUArchitectureGFX::GFX950) == 8);
}
SECTION("GFX950 write operations")
{
auto ldsWrite = MemoryOpLDS{LdsDirection::Write};
CHECK(getThreadsPerClock(ldsWrite, 1, GPUArchitectureGFX::GFX950) == 16);
CHECK(getThreadsPerClock(ldsWrite, 2, GPUArchitectureGFX::GFX950) == 8);
CHECK(getThreadsPerClock(ldsWrite, 3, GPUArchitectureGFX::GFX950) == 4);
CHECK(getThreadsPerClock(ldsWrite, 4, GPUArchitectureGFX::GFX950) == 4);
}
SECTION("Non-GFX950 operations")
{
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
CHECK(getThreadsPerClock(ldsRead, 1, GPUArchitectureGFX::GFX942) == 16);
CHECK(getThreadsPerClock(ldsRead, 2, GPUArchitectureGFX::GFX942) == 8);
CHECK(getThreadsPerClock(ldsRead, 3, GPUArchitectureGFX::GFX942) == 4);
CHECK(getThreadsPerClock(ldsRead, 4, GPUArchitectureGFX::GFX942) == 4);
}
SECTION("Invalid dword count")
{
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
CHECK_THROWS_AS(getThreadsPerClock(ldsRead, 5, GPUArchitectureGFX::GFX950), FatalError);
}
}
TEST_CASE("LDS model make bank to address counts", "[lds-bank-model]")
{
SECTION("Bank conflicts")
{
// Test multiple addresses accessing the same bank
std::vector<size_t> addresses = {0, 128, 256}; // x / 4 mod 32 = 0 for all addresses
uint dwords = 1;
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
auto bankCounts
= createBankToAddressCounts(addresses, dwords, GPUArchitectureGFX::GFX942, ldsRead);
CHECK(bankCounts.size() == 1);
CHECK(bankCounts[0] == 3);
auto more = {4, 16};
addresses.insert(addresses.end(), more.begin(), more.end());
bankCounts
= createBankToAddressCounts(addresses, dwords, GPUArchitectureGFX::GFX942, ldsRead);
CHECK(bankCounts.size() == 3);
CHECK(bankCounts[0] == 3);
CHECK(bankCounts[1] == 1); // 4 / 4 mod 32 = 1
CHECK(bankCounts[4] == 1); // 16 / 4 mod 32 = 4
}
SECTION("Wrap around")
{
std::vector<size_t> addresses = {124}; // 124 / 4 mod 32 = 31
uint dwords = 4; // Accesses banks 31, 0, 1, 2
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
auto bankCounts
= createBankToAddressCounts(addresses, dwords, GPUArchitectureGFX::GFX942, ldsRead);
CHECK(bankCounts.size() == 4);
CHECK(bankCounts[31] == 1);
CHECK(bankCounts[0] == 1);
CHECK(bankCounts[1] == 1);
CHECK(bankCounts[2] == 1);
}
}
TEST_CASE("LDS model calculate bank conflict cycles", "[lds-bank-model]")
{
std::map<uint, uint> bankToAddressCounts = {};
CHECK(calculateBankConflictCycles(bankToAddressCounts) == 0);
bankToAddressCounts = {{0, 1}};
CHECK(calculateBankConflictCycles(bankToAddressCounts) == 1);
bankToAddressCounts = {
{0, 2},
{1, 3},
{2, 1},
{5, 3},
{10, 2},
};
CHECK(calculateBankConflictCycles(bankToAddressCounts) == 3);
}
TEST_CASE("LDS model divide into thread groups", "[lds-bank-model]")
{
std::vector<size_t> addresses = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44};
uint threadsPerClock = 4;
auto groups = divideIntoThreadGroups(addresses, threadsPerClock);
// 3 groups of 4 addresses each
CHECK(groups.size() == 3);
CHECK(groups[0].size() == 4);
CHECK(groups[1].size() == 4);
CHECK(groups[2].size() == 4);
CHECK(groups[0][0] == 0);
CHECK(groups[0][1] == 4);
CHECK(groups[0][2] == 8);
CHECK(groups[0][3] == 12);
CHECK(groups[1][0] == 16);
CHECK(groups[1][1] == 20);
CHECK(groups[1][2] == 24);
CHECK(groups[1][3] == 28);
CHECK(groups[2][0] == 32);
CHECK(groups[2][1] == 36);
CHECK(groups[2][2] == 40);
CHECK(groups[2][3] == 44);
}
TEST_CASE("LDS model compute thread group bank mappings", "[lds-bank-model]")
{
RuntimeLDSInstruction instr;
instr.memoryOp = MemoryOpLDS{LdsDirection::Read};
instr.dwords = 4;
for(int i = 0; i < 64; ++i) // 64 threads
{
// No bank conflicts should occur
instr.baseAddresses.push_back(i * 4 * instr.dwords);
}
auto mappings = computeThreadGroupBankMappings(instr, GPUArchitectureGFX::GFX950);
CHECK(mappings.size() == 8); // 64 threads / 8 threads per clock = 8 groups
for(size_t i = 0; i < mappings.size(); ++i)
{
CHECK(mappings[i].size() == 32); // 8 threads * 4 dwords each = 32 banks accessed
for(const auto& [bank, count] : mappings[i])
{
CHECK(count == 1);
}
}
}
TEST_CASE("LDS model calculate total cycles from bank mappings", "[lds-bank-model]")
{
std::vector<std::map<uint, uint>> mappings = {
{{0, 5}, {1, 2}, {2, 1}}, // Group 1: max 5 accesses
{{3, 1}, {4, 1}, {5, 1}}, // Group 2: max 1 access (no conflicts)
{{0, 3}, {1, 3}, {2, 3}} // Group 3: max 3 accesses
};
// 5 + 1 + 3 = 9 cycles
CHECK(calculateTotalCyclesFromBankMappings(mappings) == 9);
}
TEST_CASE("LDS model get clock count", "[lds-bank-model]")
{
RuntimeLDSInstruction instr;
instr.dwords = 2;
instr.baseAddresses = {0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120};
const auto gfx = GPUArchitectureGFX::GFX942;
const auto mappings = computeThreadGroupBankMappings(instr, gfx);
const auto baseCycles = calculateTotalCyclesFromBankMappings(mappings);
SECTION("Read")
{
instr.memoryOp = MemoryOpLDS{LdsDirection::Read};
const auto cycles = getInstructionDataCycles(instr, gfx);
CHECK(cycles == baseCycles);
}
SECTION("Write")
{
instr.memoryOp = MemoryOpLDS{LdsDirection::Write};
const auto cycles = getInstructionDataCycles(instr, gfx);
CHECK(cycles == baseCycles);
}
}
TEST_CASE("LDS model get instruction issue cycles", "[lds-bank-model]")
{
SECTION("Read operations")
{
auto ldsRead = MemoryOpLDS{LdsDirection::Read};
// For reads, always 4 cycles (address transfer only)
for(uint dwords = 1; dwords <= 4; ++dwords)
{
CHECK(getInstructionIssueCycles(ldsRead, dwords) == 4);
}
}
SECTION("Write operations")
{
auto ldsWrite = MemoryOpLDS{LdsDirection::Write};
// For writes, 4 cycles (address) + 4 cycles per dword (data)
for(uint dwords = 1; dwords <= 4; ++dwords)
{
CHECK(getInstructionIssueCycles(ldsWrite, dwords) == 4 + 4 * dwords);
}
}
}
TEST_CASE("LDS model sample addresses", "[lds-bank-model]")
{
// Sample taken from a GEMM kernel through rocgdb
// Note: stride is not always 64 even though the first few appear to be so
std::vector<size_t> addresses
= {0, 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960,
16, 80, 144, 208, 272, 336, 400, 464, 528, 592, 656, 720, 784, 848, 912, 976,
32, 96, 160, 224, 288, 352, 416, 480, 544, 608, 672, 736, 800, 864, 928, 992,
48, 112, 176, 240, 304, 368, 432, 496, 560, 624, 688, 752, 816, 880, 944, 1008};
RuntimeLDSInstruction instr = {
.memoryOp = MemoryOpLDS{LdsDirection::Read}, .dwords = 4, .baseAddresses = addresses};
auto expectedCycles = getInstructionDataCycles(instr, GPUArchitectureGFX::GFX950);
CHECK(expectedCycles == 16); // Compared to same kernel through rocprofv3
}
TEST_CASE("LDS model data cycle predictions", "[lds-bank-model]")
{
SECTION("ds_read_b32 stride 1")
{
const int dwords = 1;
const int stride = 1;
const int workgroupSize = 64;
CHECK(getInstructionDataCycles(
{.memoryOp = MemoryOpLDS{LdsDirection::Read},
.dwords = dwords,
.baseAddresses = generateLDSAddresses(workgroupSize, stride, dwords)},
GPUArchitectureGFX::GFX950)
== 4);
}
SECTION("ds_read_b32 stride 2")
{
const int dwords = 1;
const int stride = 2;
const int workgroupSize = 64;
CHECK(getInstructionDataCycles(
{.memoryOp = MemoryOpLDS{LdsDirection::Read},
.dwords = dwords,
.baseAddresses = generateLDSAddresses(workgroupSize, stride, dwords)},
GPUArchitectureGFX::GFX950)
== 4);
}
}
TEST_CASE("hasNonOverlappingBankAccess", "[lds-bank-model]")
{
SECTION("ds_read_b64 stride 1 wgs 256")
{
const int dwords = 2;
RuntimeLDSInstruction instr;
instr.memoryOp.direction = LdsDirection::Read;
instr.dwords = dwords;
instr.baseAddresses = generateLDSAddresses(256, 1, dwords);
CHECK(hasNonOverlappingBankAccess(instr, GPUArchitectureGFX::GFX950));
}
SECTION("ds_read_b64 stride 2 wgs 256")
{
const int dwords = 2;
RuntimeLDSInstruction instr;
instr.memoryOp.direction = LdsDirection::Read;
instr.dwords = dwords;
instr.baseAddresses = generateLDSAddresses(256, 2, dwords);
CHECK_FALSE(hasNonOverlappingBankAccess(instr, GPUArchitectureGFX::GFX950));
}
}
}