// Copyright © Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include "get_handle.hpp" #include "gtest_common.hpp" #include "../lib_env_var.hpp" #include "../workspace.hpp" #include #include #include #include #include #include #include #include #include namespace { struct Find2ConvTestCase { miopenProblemDirection_t direction; int tune; bool preallocate; std::size_t workspace_limit; bool attach_binaries; friend std::ostream& operator<<(std::ostream& os, const Find2ConvTestCase& tc) { return os << "direction:" << tc.direction << " tune:" << tc.tune << " preallocate:" << tc.preallocate << " ws_limit:" << tc.workspace_limit << " attach_binaries:" << tc.attach_binaries; } }; void RunFind2ConvTest(const Find2ConvTestCase& test_case) { auto& handle_deref = get_handle(); // Generate tensors tensor x{16, 192, 28, 28}; x.generate(tensor_elem_gen_integer{17}); tensor w{32, 192, 5, 5}; w.generate(tensor_elem_gen_integer{17}); miopen::ConvolutionDescriptor filter = { 2, miopenConvolution, miopenPaddingDefault, {1, 1}, {1, 1}, {1, 1}}; tensor y{filter.GetForwardOutputTensor(x.desc, w.desc)}; auto x_dev = handle_deref.Write(x.data); auto w_dev = handle_deref.Write(w.data); auto y_dev = handle_deref.Write(y.data); // Test convolution miopenHandle_t handle = &handle_deref; miopenProblem_t problem; EXPECT_EQ(miopenCreateConvProblem(&problem, &filter, test_case.direction), miopenStatusSuccess); // Add tensor descriptors EXPECT_EQ(miopenSetProblemTensorDescriptor(problem, miopenTensorConvolutionX, &x.desc), miopenStatusSuccess); EXPECT_EQ(miopenSetProblemTensorDescriptor(problem, miopenTensorConvolutionW, &w.desc), miopenStatusSuccess); EXPECT_EQ(miopenSetProblemTensorDescriptor(problem, miopenTensorConvolutionY, &y.desc), miopenStatusSuccess); // Adding x descriptor again to validate that error is produced EXPECT_EQ(miopenSetProblemTensorDescriptor(problem, miopenTensorConvolutionX, &x.desc), miopenStatusBadParm); // Test FindSolutions auto solutions = std::vector{}; std::size_t found = 0; solutions.resize(100); EXPECT_EQ( miopenFindSolutions(handle, problem, nullptr, solutions.data(), &found, solutions.size()), miopenStatusSuccess); EXPECT_GE(found, 0); solutions.resize(found); // Test FindSolutions with options solutions.resize(100); found = 0; { miopenFindOptions_t options; EXPECT_EQ(miopenCreateFindOptions(&options), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionTuning(options, test_case.tune), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionResultsOrder(options, miopenFindResultsOrderByTime), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionWorkspaceLimit(options, test_case.workspace_limit), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionAttachBinaries(options, test_case.attach_binaries), miopenStatusSuccess); Workspace wspace{}; // This GPU buffer may be used by miopenFindSolutions if(test_case.preallocate) { std::size_t workspace_max = [&]() { std::size_t workspace_max_ = 0; switch(test_case.direction) { case miopenProblemDirectionForward: EXPECT_EQ(miopenConvolutionForwardGetWorkSpaceSize( handle, &x.desc, &w.desc, &filter, &y.desc, &workspace_max_), miopenStatusSuccess); return workspace_max_; case miopenProblemDirectionBackward: EXPECT_EQ(miopenConvolutionBackwardDataGetWorkSpaceSize( handle, &y.desc, &w.desc, &filter, &x.desc, &workspace_max_), miopenStatusSuccess); return workspace_max_; case miopenProblemDirectionBackwardWeights: EXPECT_EQ(miopenConvolutionBackwardWeightsGetWorkSpaceSize( handle, &y.desc, &x.desc, &filter, &w.desc, &workspace_max_), miopenStatusSuccess); return workspace_max_; #ifdef MIOPEN_BETA_API case miopenProblemDirectionInference: break; #endif } MIOPEN_THROW(miopenStatusNotImplemented); }(); const auto workspace_size = std::min(test_case.workspace_limit, workspace_max); wspace.resize(workspace_size); EXPECT_EQ( miopenSetFindOptionPreallocatedWorkspace(options, wspace.ptr(), wspace.size()), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionPreallocatedTensor( options, miopenTensorConvolutionX, x_dev.get()), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionPreallocatedTensor( options, miopenTensorConvolutionW, w_dev.get()), miopenStatusSuccess); EXPECT_EQ(miopenSetFindOptionPreallocatedTensor( options, miopenTensorConvolutionY, y_dev.get()), miopenStatusSuccess); } EXPECT_EQ(miopenFindSolutions( handle, problem, options, solutions.data(), &found, solutions.size()), miopenStatusSuccess); EXPECT_EQ(miopenDestroyFindOptions(options), miopenStatusSuccess); } EXPECT_GE(found, 0); solutions.resize(found); // Test solution attributes for(const auto& solution : solutions) { float time; std::size_t workspace_size; uint64_t solver_id; miopenConvAlgorithm_t algo; EXPECT_EQ(miopenGetSolutionTime(solution, &time), miopenStatusSuccess); EXPECT_EQ(miopenGetSolutionWorkspaceSize(solution, &workspace_size), miopenStatusSuccess); EXPECT_EQ(miopenGetSolutionSolverId(solution, &solver_id), miopenStatusSuccess); EXPECT_EQ(miopenGetSolverIdConvAlgorithm(solver_id, &algo), miopenStatusSuccess); } // Test running solutions miopenTensorDescriptor_t x_desc = &x.desc, w_desc = &w.desc, y_desc = &y.desc; for(const auto& solution : solutions) { uint64_t solver_id; EXPECT_EQ(miopenGetSolutionSolverId(solution, &solver_id), miopenStatusSuccess); miopenTensorArgumentId_t names[3] = { miopenTensorConvolutionX, miopenTensorConvolutionW, miopenTensorConvolutionY}; void* buffers[3] = {x_dev.get(), w_dev.get(), y_dev.get()}; miopenTensorDescriptor_t descriptors[3] = {x_desc, w_desc, y_desc}; // Run solution std::size_t workspace_size; EXPECT_EQ(miopenGetSolutionWorkspaceSize(solution, &workspace_size), miopenStatusSuccess); Workspace wspace{workspace_size}; auto arguments = std::make_unique(3); for(auto i = 0; i < 3; ++i) { arguments[i].id = names[i]; arguments[i].descriptor = &descriptors[i]; arguments[i].buffer = buffers[i]; } EXPECT_EQ( miopenRunSolution(handle, solution, 3, arguments.get(), wspace.ptr(), wspace.size()), miopenStatusSuccess); // Save-load cycle std::size_t solution_size; EXPECT_EQ(miopenGetSolutionSize(solution, &solution_size), miopenStatusSuccess); EXPECT_GT(solution_size, 0); auto solution_binary = std::vector{}; solution_binary.resize(solution_size); EXPECT_EQ(miopenSaveSolution(solution, solution_binary.data()), miopenStatusSuccess); EXPECT_EQ(miopenDestroySolution(solution), miopenStatusSuccess); miopenSolution_t read_solution; EXPECT_EQ( miopenLoadSolution(&read_solution, solution_binary.data(), solution_binary.size()), miopenStatusSuccess); // Run loaded solution auto read_arguments = std::make_unique(3); for(auto i = 0; i < 3; ++i) { read_arguments[i].id = names[i]; read_arguments[i].descriptor = &descriptors[i]; read_arguments[i].buffer = buffers[i]; } EXPECT_EQ(miopenRunSolution( handle, read_solution, 3, read_arguments.get(), wspace.ptr(), wspace.size()), miopenStatusSuccess); EXPECT_EQ(miopenDestroySolution(read_solution), miopenStatusSuccess); } EXPECT_EQ(miopenDestroyProblem(problem), miopenStatusSuccess); } } // namespace class GPU_Find2Conv_FP32 : public testing::TestWithParam< std::tuple> { protected: void SetUp() override { prng::reset_seed(); // Parity with CTest: Set log level to 6 if(MIOPEN_LOG_LEVEL) { prev_log_level = lib_env::value(MIOPEN_LOG_LEVEL); } lib_env::update(MIOPEN_LOG_LEVEL, 6); } void TearDown() override { // Reset internal environment values as per Wiki guidelines if(prev_log_level.has_value()) { lib_env::update(MIOPEN_LOG_LEVEL, *prev_log_level); } else { lib_env::clear(MIOPEN_LOG_LEVEL); } } private: std::optional prev_log_level; }; // TEST_INFO is "Find2ConvTest" for descriptiveness. // Specificity is provided by Parameter Name Generator in the test output. TEST_P(GPU_Find2Conv_FP32, Find2ConvTest) { auto param = GetParam(); Find2ConvTestCase tc{std::get<0>(param), std::get<1>(param), std::get<2>(param), std::get<3>(param), std::get<4>(param)}; RunFind2ConvTest(tc); } inline std::string GetFind2ConvTestCaseName(const testing::TestParamInfo& info) { Find2ConvTestCase tc{std::get<0>(info.param), std::get<1>(info.param), std::get<2>(info.param), std::get<3>(info.param), std::get<4>(info.param)}; std::ostringstream os; os << tc; std::string name = os.str(); std::replace_if(name.begin(), name.end(), [](char c) { return !std::isalnum(c); }, '_'); return name; } INSTANTIATE_TEST_SUITE_P(Smoke, GPU_Find2Conv_FP32, testing::Combine(testing::Values(miopenProblemDirectionForward, miopenProblemDirectionBackward, miopenProblemDirectionBackwardWeights), testing::Values(0, 1), testing::Values(false, true), testing::Values(std::numeric_limits::max(), static_cast(0)), testing::Values(false, true)), GetFind2ConvTestCaseName);