/******************************************************************************* * * MIT License * * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. * * 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 #include #include #include #include #include namespace TensileLite { /** * \ingroup SolutionLibrary * * Uses a distance function to select solutions based on benchmarks. * Benchmarks are performed to determine the optimal solution at a number of * specific sizes. At runtime, we find the benchmarked size that is closest * to the size asked for. */ template struct ProblemPredictionLibrary : public SolutionLibrary { std::vector>> solution_list; std::vector origami_config_list; mutable std::atomic lastFindTopRetAll = false; static std::string Type() { return "Prediction"; } virtual std::string type() const override { return Type(); } virtual std::string description() const override { if(solution_list.empty()) return concatenate(type(), ", solution_list: empty"); return concatenate(type(), solution_list.size()); } virtual std::shared_ptr getSolutionByIndex(MyProblem const& problem, Hardware const& hardware, const int index) const override { auto indexMatch = std::find_if(solution_list.begin(), solution_list.end(), [&index](auto& s){ return s.first == index; }); if(indexMatch != solution_list.end()) return indexMatch->second; return nullptr; } virtual std::shared_ptr findBestSolution(MyProblem const& problem, Hardware const& hardware, double* fitness = nullptr) const override { auto topSolutions = findTopSolutions(problem, hardware, 1); std::shared_ptr solution; if(!topSolutions.empty()) { solution = topSolutions[0]; } return solution; } virtual SolutionSet findAllSolutions(MyProblem const& problem, Hardware const& hardware, SolutionLibrarySearchType searchType = SolutionLibrarySearchType::DEFAULT) const override { bool debug = Debug::Instance().printPropertyEvaluation(); SolutionSet rv; if(searchType == SolutionLibrarySearchType::DEFAULT) return rv; for(auto const& row : this->solution_list) { if(debug) std::cout << row.second->description() << std::endl; rv.insert(row.second); } return rv; } virtual SolutionSet findAllSolutionsGroupedGemm(std::vector const& problems, Hardware const& hardware, SolutionLibrarySearchType searchType = SolutionLibrarySearchType::DEFAULT) const override { bool debug = Debug::Instance().printPropertyEvaluation(); SolutionSet rv; if(searchType == SolutionLibrarySearchType::DEFAULT) return rv; for(auto const& row : this->solution_list) { if(debug) std::cout << row.second->description() << std::endl; rv.insert(row.second); } return rv; } virtual SolutionVector findTopSolutions(MyProblem const& problem, Hardware const& hardware, int numSolutions) const override { SolutionVector rv; size_t m = 1; size_t n = 1; size_t k = 1; size_t batch = 1; for(size_t i = 0; i < problem.freeIndicesA().size(); i++) { m *= problem.freeSizeA(i); } for(size_t i = 0; i < problem.freeIndicesB().size(); i++) { n *= problem.freeSizeB(i); } for(size_t i = 0; i < problem.boundIndices().size(); ++i) { k *= problem.boundSize(i); } for(size_t i = 0; i < problem.batchIndices().size(); ++i) { batch *= problem.batchSize(i); } hip::HipAMDGPU const* pAMDGPU = dynamic_cast(&hardware); const bool debug = Debug::Instance().printPropertyEvaluation(); auto considerSolution = [&](std::shared_ptr const& solution) { const bool hwMatch = (*(solution->hardwarePredicate))(hardware); const bool probMatch = (*(solution->problemPredicate))(problem); const bool predicateMatch = hwMatch && probMatch; if(debug) { PredicateDebugger::printHeader( std::cout, "Prediction: " + solution->name()); solution->hardwarePredicate->debugEval(hardware, std::cout); solution->problemPredicate->debugEval(problem, std::cout); PredicateDebugger::printFooter(std::cout, predicateMatch); } if(predicateMatch) { rv.emplace_back(solution); } }; if(pAMDGPU && pAMDGPU->analyticalHardware) { auto miDataType = datatypeToAnalyticalDatatype(problem.computeInputTypeA()); if(problem.f32XdlMathOp() == rocisa::DataType::XFloat32) // Check F32 compute type miDataType = origami::data_type_t::XFloat32; origami::problem_t origami_problem = { .size = {m, n, k}, .batch = batch, // CU budget hint; 0 = use all CUs. .num_cus = static_cast(problem.getParams().smCountTarget()), .a_transpose = problem.transA() ? origami::transpose_t::T : origami::transpose_t::N, .b_transpose = problem.transB() ? origami::transpose_t::T : origami::transpose_t::N, .a_dtype = datatypeToAnalyticalDatatype(problem.a().dataType()), .b_dtype = datatypeToAnalyticalDatatype(problem.b().dataType()), .c_dtype = datatypeToAnalyticalDatatype(problem.c().dataType()), .d_dtype = datatypeToAnalyticalDatatype(problem.d().dataType()), .mi_dtype = miDataType, .a_mx_block_size = 0, // MX Data types come from rocroller .b_mx_block_size = 0, // MX Data types come from rocroller }; auto prediction_result = origami::rank_configs( origami_problem, *(pAMDGPU->analyticalHardware), origami_config_list); for(const auto& r : prediction_result) { if(r.config.index >= solution_list.size()) { continue; } considerSolution(solution_list[r.config.index].second); if(rv.size() == numSolutions) { break; } } } // can't reach the requested number, means findTop already done its best lastFindTopRetAll = (rv.size() < numSolutions); return rv; } virtual bool lastFindTopAlreadyRetAll() const override { return lastFindTopRetAll; } virtual SolutionVector findTopSolutionsGroupedGemm(std::vector const& problems, Hardware const& hardware, int numSolutions) const override { SolutionVector solutions; return solutions; } }; } // namespace TensileLite