/******************************************************************************* * * MIT License * * Copyright 2025-2026 AMD ROCm(TM) Software * * 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 #include #include "origami/gemm.hpp" #include "origami/hardware.hpp" #include "origami/heuristics.hpp" #include "origami/origami.hpp" #include "origami/streamk.hpp" // Uses _putenv_s on Windows, setenv on POSIX inline int portable_setenv(const char* name, const char* value, int overwrite) { #ifdef _WIN32 (void)overwrite; return _putenv_s(name, value); #else return setenv(name, value, overwrite); #endif } inline int portable_unsetenv(const char* name) { #ifdef _WIN32 return _putenv_s(name, ""); #else return unsetenv(name); #endif } // List of GPU architectures to test inline const std::vector test_architectures = {942, 950, 1250}; // Helper function to construct problem_t inline origami::problem_t make_problem(size_t m, size_t n, size_t k, origami::transpose_t a_trans = origami::transpose_t::T, origami::transpose_t b_trans = origami::transpose_t::N, size_t batch = 1, int mx_block_size = 0, size_t q_heads = 32) { origami::problem_t problem; problem.size.m = m; problem.size.n = n; problem.size.k = k; problem.batch = batch; problem.q_heads = q_heads; problem.a_transpose = a_trans; problem.b_transpose = b_trans; problem.a_dtype = origami::data_type_t::BFloat16; problem.b_dtype = origami::data_type_t::BFloat16; problem.c_dtype = origami::data_type_t::BFloat16; problem.d_dtype = origami::data_type_t::BFloat16; problem.mi_dtype = origami::data_type_t::BFloat16; problem.a_mx_block_size = mx_block_size; problem.b_mx_block_size = mx_block_size; return problem; } // Helper function to construct config_t inline origami::config_t make_config(size_t mt_m, size_t mt_n, size_t mt_k, size_t mi_m = 16, size_t mi_n = 16, size_t mi_k = 16, bool hand_optimized_main_loop = false, int wgm = 1, int occupancy = 1, int non_temporal_a = 0, int non_temporal_b = 0) { origami::config_t config; config.mt.m = mt_m; config.mt.n = mt_n; config.mt.k = mt_k; config.mi.m = mi_m; config.mi.n = mi_n; config.mi.k = mi_k; config.hand_optimized_main_loop = hand_optimized_main_loop; config.occupancy = occupancy; config.workgroup_mapping = wgm; config.cache_hints_a = non_temporal_a; config.cache_hints_b = non_temporal_b; return config; } // Helper function to construct hardware_t with all parameters inline origami::hardware_t make_hardware(int gpu_arch) { // Initialize the constants size_t n_cu = 0; size_t lds_capacity = 0; size_t rf_capacity = 0; size_t num_xcd = 0; double mem1_perf_ratio = 0.0; double mem2_perf_ratio = 0.0; double mem3_perf_ratio = 0.0; size_t l2_capacity = 0; double compute_clock_ghz = 0.0; size_t parallel_mi_cu = 0; std::tuple mem_bw_per_wg_coefficients = std::make_tuple(0, 0, 0); if (gpu_arch == 942) { n_cu = 304; lds_capacity = 65536; rf_capacity = 512 * 1024; // 512 KB per CU for CDNA2 (gfx942) num_xcd = 8; mem1_perf_ratio = 1.0; mem2_perf_ratio = 1.0; mem3_perf_ratio = 1.0; l2_capacity = 4000000; compute_clock_ghz = 1; parallel_mi_cu = 1; mem_bw_per_wg_coefficients = std::make_tuple(0, 0.015, 0); } else if (gpu_arch == 950) { n_cu = 256; lds_capacity = 163840; rf_capacity = 512 * 1024; // 512 KB per CU for CDNA3 num_xcd = 8; mem1_perf_ratio = 1.0; mem2_perf_ratio = 1.0; mem3_perf_ratio = 1.0; l2_capacity = 4000000; compute_clock_ghz = 1.2; parallel_mi_cu = 1; mem_bw_per_wg_coefficients = std::make_tuple(0, 0.008, 0); } else if(gpu_arch == 1250) { // TODO: using gfx950 placeholders for most fields, update lds_capacity and l2_capacity later auto hw = make_hardware(950); hw.arch = origami::hardware_t::architecture_t::gfx1250; hw.mem_bw_per_wg_coefficients = std::make_tuple(0, 0.016, 0); return hw; } const std::string gpu_arch_str = "gfx" + std::to_string(gpu_arch); auto gpu_arch_enum = origami::hardware_t::arch_name_to_enum(gpu_arch_str); return origami::hardware_t(gpu_arch_enum, n_cu, lds_capacity, rf_capacity, num_xcd, mem1_perf_ratio, mem2_perf_ratio, mem3_perf_ratio, l2_capacity, compute_clock_ghz, parallel_mi_cu, mem_bw_per_wg_coefficients); }