Files
TheRock/rocm-libraries/shared/origami/python/tests/test_origami.py
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

546 lines
19 KiB
Python

# Copyright Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""Tests for origami configuration selection."""
import pytest
import origami
import csv
from helpers import create_config_list
@pytest.mark.integration
def test_select_config_basic(hardware):
"""Test basic configuration selection."""
configs = create_config_list(hardware, "f16")
assert len(configs) > 0
problem = origami.problem_t()
problem.size = origami.dim3_t(2048, 2048, 2048)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = problem.a_dtype
problem.a_mx_block_size = 0
problem.b_mx_block_size = 0
result = origami.select_config(problem, hardware, configs, origami.model_t.gemm)
assert result.latency > 0
assert result.config is not None
assert result.config.mt.m > 0
assert result.config.mt.n > 0
assert result.config.mt.k > 0
@pytest.mark.integration
def test_rank_configs(hardware):
"""Test ranking multiple configurations."""
configs = create_config_list(hardware, "f16")
assert len(configs) > 0
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = problem.a_dtype
ranked_configs = origami.rank_configs(problem, hardware, configs, origami.model_t.gemm)
assert len(ranked_configs) > 0
assert len(ranked_configs) <= len(configs)
# Check that results are sorted by latency (best first)
for i in range(len(ranked_configs) - 1):
assert ranked_configs[i].latency <= ranked_configs[i + 1].latency
@pytest.mark.integration
def test_select_config_mnk(hardware):
"""Test select_config_mnk function."""
configs = create_config_list(hardware, "f16")
assert len(configs) > 0
result = origami.select_config_mnk(2048, 2048, 2048, hardware, configs)
assert result.latency > 0
assert result.config is not None
@pytest.mark.integration
def test_select_topk_configs(hardware):
"""Test select_topk_configs function."""
configs = create_config_list(hardware, "f16")
assert len(configs) > 0
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = problem.a_dtype
topk = 5
top_configs = origami.select_topk_configs(problem, hardware, configs, topk, origami.model_t.gemm)
assert len(top_configs) <= topk
assert len(top_configs) > 0
# Check that results are sorted by latency (best first)
for i in range(len(top_configs) - 1):
assert top_configs[i].latency <= top_configs[i + 1].latency
@pytest.mark.integration
@pytest.mark.slow
def test_select_config_with_csv(tmp_path, hardware):
"""Test configuration selection with CSV input file."""
# Create a test CSV file
csv_file = tmp_path / "test_sizes.csv"
with open(csv_file, "w") as f:
writer = csv.writer(f)
writer.writerow([2048, 2048, 1, 2048])
writer.writerow([4096, 4096, 1, 4096])
configs = create_config_list(hardware, "f16")
with open(csv_file, "r") as csvfile:
csv_reader = csv.reader(csvfile)
for row in csv_reader:
M = int(row[0])
N = int(row[1])
B = int(row[2])
K = int(row[3])
problem = origami.problem_t()
problem.size = origami.dim3_t(M, N, K)
problem.batch = B
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = problem.a_dtype
best_config = origami.select_config(problem, hardware, configs, origami.model_t.gemm)
assert best_config.latency > 0
assert best_config.config is not None
@pytest.mark.integration
def test_hardware_print(hardware):
"""Test hardware print functionality."""
# This should not raise an exception
hardware.print()
@pytest.mark.integration
def test_compute_perf_gflops(hardware):
"""Test compute_perf_gflops function."""
configs = create_config_list(hardware, "f16")
problem = origami.problem_t()
problem.size = origami.dim3_t(2048, 2048, 2048)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = problem.a_dtype
result = origami.select_config(problem, hardware, configs, origami.model_t.gemm)
gflops = origami.compute_perf_gflops(hardware, problem, result.latency)
assert gflops > 0
# StreamK Grid Selection Tests
@pytest.mark.integration
def test_select_grid_size(hardware):
"""Test grid size selection."""
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = origami.string_to_datatype("f16")
problem.a_mx_block_size = 0
problem.b_mx_block_size = 0
config = origami.config_t()
config.mt = origami.dim3_t(32, 32, 256)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 1
config.workgroup_mapping = 6
grid_size = origami.select_grid_size(
problem, hardware, config, origami.grid_selection_t.analytical
)
assert grid_size > 0
@pytest.mark.integration
def test_select_reduction(hardware):
"""Test reduction strategy selection."""
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = origami.string_to_datatype("f16")
config = origami.config_t()
config.mt = origami.dim3_t(32, 32, 256)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 1
config.workgroup_mapping = 6
reduction = origami.select_reduction(
problem, hardware, config, origami.grid_selection_t.analytical
)
assert reduction is not None
@pytest.mark.integration
@pytest.mark.parametrize(
"algorithm",
[
origami.grid_selection_t.number_of_cus,
origami.grid_selection_t.min_resources,
origami.grid_selection_t.energy_aware,
origami.grid_selection_t.reduction_cost_aware,
origami.grid_selection_t.data_parallel,
origami.grid_selection_t.analytical,
origami.grid_selection_t.k_split_aware,
],
)
def test_select_grid_size_algorithms(hardware, algorithm):
"""Test different grid selection algorithms."""
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = origami.string_to_datatype("f16")
config = origami.config_t()
config.mt = origami.dim3_t(32, 32, 256)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 1
config.workgroup_mapping = 6
grid_size = origami.select_grid_size(problem, hardware, config, algorithm)
assert grid_size > 0
@pytest.mark.integration
def test_compute_number_of_output_tiles():
"""Test compute_number_of_output_tiles function."""
num_tiles = origami.compute_number_of_output_tiles(32, 32, 8192, 8192, 1)
assert num_tiles > 0
assert num_tiles == (8192 // 32) * (8192 // 32) * 1
@pytest.mark.integration
def test_select_workgroup_mapping(hardware):
"""Test workgroup mapping selection."""
problem = origami.problem_t()
problem.size = origami.dim3_t(8192, 8192, 8192)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.string_to_datatype("f16")
problem.b_dtype = origami.string_to_datatype("f16")
problem.d_dtype = origami.string_to_datatype("f16")
problem.c_dtype = problem.d_dtype
problem.mi_dtype = origami.string_to_datatype("f16")
config = origami.config_t()
config.mt = origami.dim3_t(32, 32, 256)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 1
config.workgroup_mapping = 6
sk_grid = 100
result = origami.select_workgroup_mapping(problem, hardware, config, sk_grid)
assert isinstance(result, origami.workgroup_mapping_t)
@pytest.mark.integration
def test_gfx950_bfloat16_recommended_matrix_instruction():
"""Test that gfx950 recommends 16x16x32 matrix instruction for bfloat16."""
# Create hardware object for gfx950
hardware = origami.hardware_t(
origami.architecture_t.gfx950,
304, # N_CU
65536, # lds_capacity
512 * 1024, # rf_capacity
12, # NUM_XCD
1.0, # mem1_perf_ratio
1.0, # mem2_perf_ratio
1.0, # mem3_perf_ratio
25165824, # L2_capacity
2.1, # compute_clock_ghz
4, # parallel_mi_cu
(1.0, 1.0, 1.0), # mem_bw_per_wg_coefficients
None, # pci_chip_id
)
# Get recommended matrix instruction for bfloat16
bfloat16_dtype = origami.data_type_t.BFloat16
recommended_mi = hardware.get_recommended_matrix_instruction(bfloat16_dtype)
# Verify it's 16x16x32
assert recommended_mi.m == 16, f"Expected m=16, got {recommended_mi.m}"
assert recommended_mi.n == 16, f"Expected n=16, got {recommended_mi.n}"
assert recommended_mi.k == 32, f"Expected k=32, got {recommended_mi.k}"
# Formocast Simulation Mode Tests
@pytest.mark.integration
def test_prediction_modes_enum_exists():
"""Test that prediction_modes_t enum is available."""
assert hasattr(origami, 'prediction_modes_t')
assert hasattr(origami.prediction_modes_t, 'estimation')
assert hasattr(origami.prediction_modes_t, 'simulation')
@pytest.mark.integration
def test_config_has_formocast_fields():
"""Test that config_t has Formocast-specific fields via variant backend."""
config = origami.config_t()
# Check prediction_mode field
assert hasattr(config, 'prediction_mode')
# Check tensile accessor method exists
assert hasattr(config, 'tensile')
assert callable(config.tensile)
assert hasattr(config, 'has_tensile_params')
assert hasattr(config, 'set_tensile_params')
# Get tensile params (initializes if not set)
tensile = config.tensile()
# Check vectorization fields on config (generic, not Tensile-specific)
assert hasattr(config, 'grvw_a')
assert hasattr(config, 'grvw_b')
assert hasattr(config, 'gwvw_d')
assert hasattr(config, 'vector_width_a')
assert hasattr(config, 'vector_width_b')
# Check Tensile-specific fields
assert hasattr(tensile, 'depth_u')
assert hasattr(tensile, 'global_split_u')
assert hasattr(tensile, 'global_accumulation')
assert hasattr(tensile, 'local_split_u')
assert hasattr(tensile, 'direct_to_vgpr_a')
assert hasattr(tensile, 'direct_to_vgpr_b')
assert hasattr(tensile, 'wave_num')
assert hasattr(tensile, 'wave_group_m')
assert hasattr(tensile, 'wave_group_n')
assert hasattr(tensile, 'prefetch_global_read')
# Verify has_tensile_params returns True after accessing
assert config.has_tensile_params()
@pytest.mark.integration
def test_simulation_mode_returns_valid_latency():
"""Test that simulation mode returns a valid positive latency."""
# Create hardware for gfx942
hardware = origami.hardware_t(
origami.architecture_t.gfx942,
304, # N_CU
65536, # lds_capacity
512 * 1024, # rf_capacity
8, # NUM_XCD
1.0, # mem1_perf_ratio
1.0, # mem2_perf_ratio
1.0, # mem3_perf_ratio
4000000, # L2_capacity
1.5, # compute_clock_ghz
1, # parallel_mi_cu
(0.0, 0.015, 0.0), # mem_bw_per_wg_coefficients
None,
)
# Create problem
problem = origami.problem_t()
problem.size = origami.dim3_t(2048, 2048, 2048)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.data_type_t.Half
problem.b_dtype = origami.data_type_t.Half
problem.c_dtype = origami.data_type_t.Half
problem.d_dtype = origami.data_type_t.Half
problem.mi_dtype = origami.data_type_t.Half
# Create config with simulation mode
config = origami.config_t()
config.mt = origami.dim3_t(128, 128, 32)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 2
config.workgroup_mapping = 8
config.prediction_mode = origami.prediction_modes_t.simulation
# Set Formocast-specific parameters (via tensile nested struct)
config.tensile().depth_u = 32
config.tensile().global_split_u = 1
config.grvw_a = 4
config.grvw_b = 4
config.gwvw_d = 4
config.tensile().wave_num = 4
config.tensile().wave_group_m = 2
config.tensile().wave_group_n = 2
config.tensile().prefetch_global_read = 2
# Call compute_total_latency with simulation mode
latency = origami.compute_total_latency(problem, hardware, config)
assert latency > 0, f"Expected positive latency, got {latency}"
@pytest.mark.integration
def test_simulation_mode_via_compute_total_latency():
"""Test that compute_total_latency uses Formocast when prediction_mode is simulation."""
# Create hardware for gfx942
hardware = origami.hardware_t(
origami.architecture_t.gfx942,
304, # N_CU
65536, # lds_capacity
512 * 1024, # rf_capacity
8, # NUM_XCD
1.0, # mem1_perf_ratio
1.0, # mem2_perf_ratio
1.0, # mem3_perf_ratio
4000000, # L2_capacity
1.5, # compute_clock_ghz
1, # parallel_mi_cu
(0.0, 0.015, 0.0), # mem_bw_per_wg_coefficients
None,
)
# Create problem
problem = origami.problem_t()
problem.size = origami.dim3_t(2048, 2048, 2048)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.data_type_t.Half
problem.b_dtype = origami.data_type_t.Half
problem.c_dtype = origami.data_type_t.Half
problem.d_dtype = origami.data_type_t.Half
problem.mi_dtype = origami.data_type_t.Half
# Create config with estimation mode
config_estimation = origami.config_t()
config_estimation.mt = origami.dim3_t(128, 128, 32)
config_estimation.mi = origami.dim3_t(16, 16, 16)
config_estimation.occupancy = 2
config_estimation.workgroup_mapping = 8
config_estimation.prediction_mode = origami.prediction_modes_t.estimation
# Create config with simulation mode
config_simulation = origami.config_t()
config_simulation.mt = origami.dim3_t(128, 128, 32)
config_simulation.mi = origami.dim3_t(16, 16, 16)
config_simulation.occupancy = 2
config_simulation.workgroup_mapping = 8
config_simulation.prediction_mode = origami.prediction_modes_t.simulation
config_simulation.tensile().depth_u = 32
config_simulation.tensile().global_split_u = 1
config_simulation.grvw_a = 4
config_simulation.grvw_b = 4
config_simulation.gwvw_d = 4
config_simulation.tensile().wave_num = 4
config_simulation.tensile().wave_group_m = 2
config_simulation.tensile().wave_group_n = 2
config_simulation.tensile().prefetch_global_read = 2
# Get latencies from both modes
latency_estimation = origami.compute_total_latency(
problem, hardware, config_estimation)
latency_simulation = origami.compute_total_latency(
problem, hardware, config_simulation)
# Both should return positive values
assert latency_estimation > 0, f"Estimation latency should be positive: {latency_estimation}"
assert latency_simulation > 0, f"Simulation latency should be positive: {latency_simulation}"
# The values should be different (different models)
# Note: They use different units (cycles vs microseconds), so they will definitely differ
assert latency_estimation != latency_simulation, \
"Estimation and simulation should produce different results"
@pytest.mark.integration
@pytest.mark.parametrize("m,n,k", [
(1024, 1024, 1024),
(2048, 2048, 2048),
(4096, 4096, 512),
(512, 4096, 4096),
])
def test_simulation_mode_various_problem_sizes(m, n, k):
"""Test simulation mode with various problem sizes."""
# Create hardware for gfx942
hardware = origami.hardware_t(
origami.architecture_t.gfx942,
304, 65536, 512 * 1024, 8, 1.0, 1.0, 1.0, 4000000, 1.5, 1, (0.0, 0.015, 0.0), None
)
problem = origami.problem_t()
problem.size = origami.dim3_t(m, n, k)
problem.batch = 1
problem.a_transpose = origami.transpose_t.T
problem.b_transpose = origami.transpose_t.N
problem.a_dtype = origami.data_type_t.Half
problem.b_dtype = origami.data_type_t.Half
problem.c_dtype = origami.data_type_t.Half
problem.d_dtype = origami.data_type_t.Half
problem.mi_dtype = origami.data_type_t.Half
config = origami.config_t()
config.mt = origami.dim3_t(128, 128, 32)
config.mi = origami.dim3_t(16, 16, 16)
config.occupancy = 2
config.workgroup_mapping = 8
config.prediction_mode = origami.prediction_modes_t.simulation
config.tensile().depth_u = 32
config.tensile().global_split_u = 1
config.grvw_a = 4
config.grvw_b = 4
config.gwvw_d = 4
config.tensile().wave_num = 4
config.tensile().wave_group_m = 2
config.tensile().wave_group_n = 2
latency = origami.compute_total_latency(problem, hardware, config)
assert latency > 0, f"Expected positive latency for {m}x{n}x{k}, got {latency}"