Files
TheRock/rocm-libraries/shared/rocroller/test/catch/IdentifyParallelDimensionsTest.cpp
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

352 lines
15 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
#include "TestContext.hpp"
#include <rocRoller/AssemblyKernel.hpp>
#include <rocRoller/AssemblyKernelArgument.hpp>
#include <rocRoller/DataTypes/DataTypes.hpp>
#include <rocRoller/KernelGraph/Constraints.hpp>
#include <rocRoller/KernelGraph/KernelGraph.hpp>
#include <rocRoller/KernelGraph/Transforms/CleanArguments.hpp>
#include <rocRoller/KernelGraph/Transforms/IdentifyParallelDimensions.hpp>
#include <common/CommonGraphs.hpp>
TEST_CASE("identifyParallelDimensionSets works for MatrixMultiply",
"[kernel-graph][graph-transforms]")
{
using namespace rocRoller;
auto example = rocRollerTest::Graphs::MatrixMultiply(DataType::Int32);
auto kgraph = KernelGraph::translate(example.getCommand());
SECTION("reachableNodes")
{
auto sameDimensionLoadTiledNodes
= KernelGraph::loadNodesReachableWithoutDimensionModifyingNodes(kgraph.control, 9);
std::set<int> expected;
CHECK(sameDimensionLoadTiledNodes == expected);
}
SECTION("identifyParallelDimensionSets")
{
auto redundantArgs = KernelGraph::identifyParallelDimensionSets(kgraph);
std::vector<std::set<int>> expected = {{3, 9}, {2, 18}, {10, 19}};
CHECK(redundantArgs == expected);
}
}
TEST_CASE("identifyParallelDimensionSets works for GEMM", "[kernel-graph]")
{
using namespace rocRoller;
auto ctx = TestContext::ForDefaultTarget();
auto example = rocRollerTest::Graphs::GEMM(DataType::Float);
auto kgraph = KernelGraph::translate(example.getCommand());
auto redundantArgs = KernelGraph::identifyParallelDimensionSets(kgraph);
std::vector<std::set<int>> ra2 = {{3, 9}, {2, 37}, {10, 38}, {16, 37}, {17, 38}};
CHECK(redundantArgs == ra2);
}
struct HasKernelArgMatcher : Catch::Matchers::MatcherGenericBase
{
HasKernelArgMatcher(std::string name_)
: name(std::move(name_))
{
}
bool match(std::vector<rocRoller::AssemblyKernelArgument> const& kargs) const
{
for(auto const& arg : kargs)
{
if(arg.getName().starts_with(name))
return true;
}
return false;
}
std::string describe() const override
{
return fmt::format("Has an argument starting with {}", name);
}
std::string name;
};
auto HasKernelArgNamed(std::string name)
{
return HasKernelArgMatcher(std::move(name));
}
SCENARIO("IdentifyParallelDimensions transformation works for MatrixMultiply", "[kernel-graph]")
{
using namespace rocRoller;
auto ctx = TestContext::ForDefaultTarget();
auto example = rocRollerTest::Graphs::MatrixMultiply(DataType::Float);
GIVEN("The initial kernel graph for a MatrixMultiply")
{
auto kgraph = KernelGraph::translate(example.getCommand());
THEN("A and B tensor sizes are added as kernel args.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
AND_THEN("The HasKernelArgNamed() matcher works.")
{
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("rosneT"));
}
}
WHEN("The IdentifyParallelDimensions transformation is applied")
{
auto ipd = KernelGraph::IdentifyParallelDimensions();
kgraph = ipd.apply(kgraph);
THEN("The K dimension size is no longer added twice.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
}
}
}
}
SCENARIO("IdentifyParallelDimensions transformation works for GEMM", "[kernel-graph]")
{
using namespace rocRoller;
auto ctx = TestContext::ForDefaultTarget();
auto example = rocRollerTest::Graphs::GEMM(DataType::Float);
GIVEN("The initial kernel graph for a GEMM")
{
auto kgraph = KernelGraph::translate(example.getCommand());
THEN("A, B, and C tensor sizes are all added as kernel args.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_4_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_4_size_1"));
AND_THEN("The HasKernelArgNamed() matcher works.")
{
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("rosneT"));
}
}
WHEN("The IdentifyParallelDimensions transformation is applied")
{
auto ipd = KernelGraph::IdentifyParallelDimensions();
kgraph = ipd.apply(kgraph);
THEN("Redundant tensor sizes between A, B, and C are no longer added as kernel args.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_4_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_4_size_1"));
}
}
}
}
SCENARIO("IdentifyParallelDimensions transformation works for scaled MatrixMultiply",
"[kernel-graph]")
{
using namespace rocRoller;
auto ctx = TestContext::ForDefaultTarget();
// MatrixMultiply computes: D = A × B (no C tensor, only inputs A,B and output D)
auto example = rocRollerTest::Graphs::MatrixMultiply(DataType::Float,
DataType::Float,
DataType::Float,
Operations::ScaleMode::Separate,
Operations::ScaleMode::Separate);
GIVEN("The initial kernel graph for a scaled MatrixMultiply")
{
auto kgraph = KernelGraph::translate(example.getCommand());
THEN("A, ScaleA, B, ScaleB, and D tensor sizes are added as kernel args.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
// A tensor dimensions (M, K)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
// ScaleA dimensions (M, K/blockSize)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
// B tensor dimensions (K, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_1"));
// ScaleB dimensions (K/blockSize, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_7_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_7_size_1"));
// D (output) tensor dimensions (M, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_11_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_11_size_1"));
}
WHEN("The IdentifyParallelDimensions transformation is applied")
{
auto ipd = KernelGraph::IdentifyParallelDimensions();
kgraph = ipd.apply(kgraph);
THEN("Redundant dimensions between A, ScaleA, B, ScaleB, and D are eliminated.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
// A tensor: M and K dimensions retained
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
// ScaleA: M matches A's M (redundant), K/blockSize unique (retained)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
// B tensor: K redundant with A, only N retained
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_5_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_1"));
// ScaleB: K/blockSize matches ScaleA (redundant), N matches B (redundant)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_7_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_7_size_1"));
// D (output): M matches A (redundant), N matches B (redundant)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_11_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_11_size_1"));
}
}
}
}
SCENARIO("IdentifyParallelDimensions transformation works for scaled GEMM", "[kernel-graph]")
{
using namespace rocRoller;
auto ctx = TestContext::ForDefaultTarget();
// GEMM computes: D = alpha*A*B + beta*C (has both C input and D output tensors)
auto example = rocRollerTest::Graphs::GEMM(DataType::Float);
example.setScaling(Operations::ScaleMode::Separate,
Operations::ScaleMode::Separate,
DataType::E8M0,
DataType::E8M0,
32);
GIVEN("The initial kernel graph for a scaled GEMM")
{
auto kgraph = KernelGraph::translate(example.getCommand());
THEN("A, ScaleA, B, ScaleB, C, and D tensor sizes are added as kernel args.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
// A tensor dimensions (M, K)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
// ScaleA dimensions (M, K/blockSize)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
// B tensor dimensions (K, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_1"));
// ScaleB dimensions (K/blockSize, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_7_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_7_size_1"));
// C (input) tensor dimensions (M, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_10_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_10_size_1"));
// D (output) tensor dimensions (M, N)
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_21_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_21_size_1"));
}
WHEN("The IdentifyParallelDimensions transformation is applied")
{
auto ipd = KernelGraph::IdentifyParallelDimensions();
kgraph = ipd.apply(kgraph);
THEN("Redundant dimensions between A, ScaleA, B, ScaleB, C, and D are eliminated.")
{
auto cleanArgs = KernelGraph::CleanArguments(ctx.get(), example.getCommand());
kgraph = cleanArgs.apply(kgraph);
// A tensor: M and K dimensions retained
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_0_size_1"));
// ScaleA: M matches A's M (redundant), K/blockSize unique (retained)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_2_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_2_size_1"));
// B tensor: K redundant with A, only N retained
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_5_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), HasKernelArgNamed("Tensor_5_size_1"));
// ScaleB: K/blockSize matches ScaleA (redundant), N matches B (redundant)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_7_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_7_size_1"));
// C (input): M matches A (redundant), N matches B (redundant)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_10_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_10_size_1"));
// D (output): M matches A (redundant), N matches B (redundant)
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_21_size_0"));
CHECK_THAT(ctx->kernel()->arguments(), !HasKernelArgNamed("Tensor_21_size_1"));
}
}
}
}