// Copyright © Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include #include #include #include "random.hpp" #include "../get_handle.hpp" namespace { class TrivialTestSolver final : public miopen::solver::conv::ConvSolver { public: static const char* FileName() { return "TrivialTestSolver"; } const std::string& SolverDbId() const override { return GetSolverDbId(); } bool IsApplicable(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription& problem) const override { return problem.GetInWidth() == 1; } miopen::solver::ConvSolution GetSolution(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&) const override { miopen::solver::ConvSolution ret; miopen::solver::KernelInfo kernel; kernel.kernel_file = FileName(); kernel.comp_options = " "; ret.construction_params.push_back(kernel); return ret; } }; struct TestConfig : miopen::solver::PerfConfigBase { std::string str; template static void Visit(Self&& self, F f) { f(self.str, "str"); } }; class SearchableTestSolver final : public miopen::solver::conv::ConvTunableSolver { public: static int searches_done() { return _serches_done; } static const char* FileName() { return "SearchableTestSolver"; } static const char* NoSearchFileName() { return "SearchableTestSolver.NoSearch"; } const std::string& SolverDbId() const override { return GetSolverDbId(); } bool IsApplicable(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&) const override { return true; } TestConfig GetDefaultPerformanceConfig(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&) const override { TestConfig config{}; config.str = NoSearchFileName(); return config; } bool IsValidPerformanceConfig(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&, const TestConfig&) const override { return true; } TestConfig Search(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&, const miopen::AnyInvokeParams&) const override { TestConfig config; config.str = FileName(); _serches_done++; return config; } miopen::solver::ConvSolution GetSolution(const miopen::ExecutionContext&, const miopen::conv::ProblemDescription&, const TestConfig& config) const override { miopen::solver::ConvSolution ret; miopen::solver::KernelInfo kernel; kernel.kernel_file = config.str; kernel.comp_options = " "; ret.construction_params.push_back(kernel); return ret; } private: static int _serches_done; // NOLINT (cppcoreguidelines-avoid-non-const-global-variables) }; // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) int SearchableTestSolver::_serches_done = 0; static miopen::solver::ConvSolution FindSolution(const miopen::ExecutionContext& ctx, const miopen::conv::ProblemDescription& problem, const miopen::fs::path& db_path) { miopen::PlainTextDb db(miopen::DbKinds::PerfDb, db_path); const auto solvers = miopen::solver::SolverContainer{}; return solvers.SearchForAllSolutions(ctx, problem, db, {}, 1).front(); } template class StaticContainer { public: inline static TInstance& Instance() { // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) static TInstance data{}; return data; } }; struct TestParams { std::string expected_kernel; std::vector in; std::function context_filter = [](miopen::ExecutionContext&) { }; }; using TestCase = std::tuple, std::vector>; // pre-check parameters and post-check parameters auto GenCases() { return std::vector{std::make_tuple, std::vector>( {TestParams{TrivialTestSolver::FileName(), {1, 1, 1, 1}, [](miopen::ExecutionContext&) {}}, TestParams{TrivialTestSolver::FileName(), {1, 1, 1, 1}, [](miopen::ExecutionContext& c) { c.do_search = true; }}, TestParams{SearchableTestSolver::NoSearchFileName(), {1, 1, 1, 2}, [](miopen::ExecutionContext& c) { c.do_search = false; }}, TestParams{SearchableTestSolver::FileName(), {1, 1, 1, 2}, [](miopen::ExecutionContext& c) { c.do_search = true; }}}, {TestParams{ SearchableTestSolver::FileName(), {1, 1, 1, 2}, [](miopen::ExecutionContext&) {}}, TestParams{SearchableTestSolver::FileName(), {1, 1, 1, 2}, [](miopen::ExecutionContext& c) { c.do_search = true; }}})}; } auto GetCases() { static auto cases = GenCases(); return cases; } } // namespace class CPU_SolverTest_NONE : public ::testing::TestWithParam { public: void SetUp() override { prng::reset_seed(); } void Run() { auto [pre_check_param, post_check_param] = GetParam(); const miopen::TempFile db_path("miopen.tests.solver"); for(auto const& param : pre_check_param) { ConstructTest(db_path, param.expected_kernel.c_str(), param.in, param.context_filter); } const auto& searchable_solver = StaticContainer::Instance(); const auto searches = SearchableTestSolver::searches_done(); // Should read in both cases: result is already in DB, solver is searchable. for(auto const& param : post_check_param) { ConstructTest(db_path, param.expected_kernel.c_str(), param.in, param.context_filter); } // Checking no more searches were done. EXPECT_EQ(searches, searchable_solver.searches_done()); } protected: void ConstructTest( const miopen::fs::path& db_path, const char* expected_kernel, const std::vector& in, const std::function& context_filler = [](miopen::ExecutionContext&) {}) const { const auto problem = miopen::conv::ProblemDescription{miopen::TensorDescriptor{miopenFloat, in}, miopen::TensorDescriptor{miopenFloat, in}, miopen::TensorDescriptor{miopenFloat, in}, miopen::ConvolutionDescriptor{}, miopen::conv::Direction::Forward}; auto ctx = miopen::ExecutionContext{}; ctx.SetStream(&get_handle()); ctx.disable_search_enforce = true; context_filler(ctx); const auto sol = FindSolution(ctx, problem, db_path); EXPECT_TRUE(sol.construction_params.size() > 0); EXPECT_EQ(sol.construction_params[0].kernel_file, std::string(expected_kernel)); } }; TEST_P(CPU_SolverTest_NONE, TestSolver) { Run(); } INSTANTIATE_TEST_SUITE_P(Smoke, CPU_SolverTest_NONE, ::testing::ValuesIn(GetCases()), [](auto const&) { return "SearchesDoneTest"; });