/******************************************************************************* * * MIT License * * Copyright (C) 2019-2022 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. * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _OPENMP #include #endif TEST(Cache, Simple) { using namespace Tensile; CacheMap cache(-1); EXPECT_EQ(-1, cache.find(5)); cache.add(7, 5); EXPECT_EQ(7, cache.find(5)); cache.add(9, 5); EXPECT_EQ(7, cache.find(5)); } TEST(Cache, Threaded) { using namespace Tensile; CacheMap cache(-1); #pragma omp parallel num_threads(32) { int seed = 0; #ifdef _OPENMP seed = omp_get_thread_num(); #endif std::uniform_int_distribution dist(0, 10); std::mt19937 rng(seed); for(int i = 0; i < 10000; i++) { int key = dist(rng); int value = key + 1; int lookup = cache.find(key); if(lookup != -1) EXPECT_EQ(lookup, value); cache.add(value, key); EXPECT_EQ(value, cache.find(key)); } } } TEST(Hashing, TensorDescriptor) { using namespace Tensile; TensorDescriptor a(DataType::Float, {15, 8, 20}); TensorDescriptor b(DataType::Int32, {15, 8, 20}); TensorDescriptor c(DataType::Float, {15, 8, 20}, {1, 15, 15 * 8}); TensorDescriptor d(DataType::Float, {15, 8, 20}, {1, 17, 19 * 8}); EXPECT_NE(std::hash()(a), std::hash()(b)); EXPECT_EQ(std::hash()(a), std::hash()(c)); EXPECT_NE(std::hash()(c), std::hash()(d)); } TEST(Hashing, ContractionProblem) { using namespace Tensile; // Test sizes ContractionProblem a = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem b = ContractionProblem::GEMM(false, true, 5, 7, 9, 7, 5, 5, 3.0, false, 5); ContractionProblem c = ContractionProblem::GEMM(false, true, 5, 7, 9, 0, 5, 5, 3.0, false, 5); ContractionProblem d = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); EXPECT_NE(std::hash()(a), std::hash()(b)); EXPECT_NE(std::hash()(a), std::hash()(c)); EXPECT_EQ(std::hash()(a), std::hash()(d)); // Test high precision accumulate flag ContractionProblem e = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem f = e; e.setHighPrecisionAccumulate(false); f.setHighPrecisionAccumulate(true); EXPECT_NE(std::hash()(e), std::hash()(f)); e.setHighPrecisionAccumulate(true); f.setHighPrecisionAccumulate(true); EXPECT_EQ(std::hash()(e), std::hash()(f)); e.setHighPrecisionAccumulate(false); f.setHighPrecisionAccumulate(false); EXPECT_EQ(std::hash()(e), std::hash()(f)); // Test kernel language flag ContractionProblem g = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem h = g; g.setKernelLanguage(KernelLanguage::Any); h.setKernelLanguage(KernelLanguage::Source); EXPECT_NE(std::hash()(g), std::hash()(h)); g.setKernelLanguage(KernelLanguage::Any); h.setKernelLanguage(KernelLanguage::Assembly); EXPECT_NE(std::hash()(g), std::hash()(h)); g.setKernelLanguage(KernelLanguage::Source); h.setKernelLanguage(KernelLanguage::Assembly); EXPECT_NE(std::hash()(g), std::hash()(h)); g.setKernelLanguage(KernelLanguage::Any); h.setKernelLanguage(KernelLanguage::Any); EXPECT_EQ(std::hash()(g), std::hash()(h)); g.setKernelLanguage(KernelLanguage::Source); h.setKernelLanguage(KernelLanguage::Source); EXPECT_EQ(std::hash()(g), std::hash()(h)); g.setKernelLanguage(KernelLanguage::Assembly); h.setKernelLanguage(KernelLanguage::Assembly); EXPECT_EQ(std::hash()(g), std::hash()(h)); // Test deterministic mode flag ContractionProblem i = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem j = i; i.setDeterministicMode(true); j.setDeterministicMode(false); EXPECT_NE(std::hash()(i), std::hash()(j)); i.setDeterministicMode(true); j.setDeterministicMode(true); EXPECT_EQ(std::hash()(i), std::hash()(j)); i.setDeterministicMode(false); j.setDeterministicMode(false); EXPECT_EQ(std::hash()(i), std::hash()(j)); // Test arithmetic unit flag ContractionProblem k = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem l = k; k.setArithmeticUnit(ArithmeticUnit::Any); l.setArithmeticUnit(ArithmeticUnit::MFMA); EXPECT_NE(std::hash()(k), std::hash()(l)); k.setArithmeticUnit(ArithmeticUnit::Any); l.setArithmeticUnit(ArithmeticUnit::VALU); EXPECT_NE(std::hash()(k), std::hash()(l)); k.setArithmeticUnit(ArithmeticUnit::VALU); l.setArithmeticUnit(ArithmeticUnit::MFMA); EXPECT_NE(std::hash()(k), std::hash()(l)); k.setArithmeticUnit(ArithmeticUnit::Any); l.setArithmeticUnit(ArithmeticUnit::Any); EXPECT_EQ(std::hash()(k), std::hash()(l)); k.setArithmeticUnit(ArithmeticUnit::VALU); l.setArithmeticUnit(ArithmeticUnit::VALU); EXPECT_EQ(std::hash()(k), std::hash()(l)); k.setArithmeticUnit(ArithmeticUnit::MFMA); l.setArithmeticUnit(ArithmeticUnit::MFMA); EXPECT_EQ(std::hash()(k), std::hash()(l)); // Test performance metric flag ContractionProblem m = ContractionProblem::GEMM(false, true, 5, 7, 9, 5, 5, 5, 3.0, false, 5); ContractionProblem n = m; m.setPerformanceMetric(PerformanceMetric::DeviceEfficiency); n.setPerformanceMetric(PerformanceMetric::CUEfficiency); EXPECT_NE(std::hash()(m), std::hash()(n)); m.setPerformanceMetric(PerformanceMetric::Auto); n.setPerformanceMetric(PerformanceMetric::CUEfficiency); EXPECT_NE(std::hash()(m), std::hash()(n)); m.setPerformanceMetric(PerformanceMetric::Auto); n.setPerformanceMetric(PerformanceMetric::DeviceEfficiency); EXPECT_NE(std::hash()(m), std::hash()(n)); m.setPerformanceMetric(PerformanceMetric::Auto); n.setPerformanceMetric(PerformanceMetric::Auto); EXPECT_EQ(std::hash()(m), std::hash()(n)); m.setPerformanceMetric(PerformanceMetric::DeviceEfficiency); n.setPerformanceMetric(PerformanceMetric::DeviceEfficiency); EXPECT_EQ(std::hash()(m), std::hash()(n)); m.setPerformanceMetric(PerformanceMetric::CUEfficiency); n.setPerformanceMetric(PerformanceMetric::CUEfficiency); EXPECT_EQ(std::hash()(m), std::hash()(n)); } TEST(Hashing, AMDGPU) { using namespace Tensile; std::vector processors{AMDGPU::Processor::gfx803, AMDGPU::Processor::gfx900, AMDGPU::Processor::gfx906, AMDGPU::Processor::gfx908, AMDGPU::Processor::gfx90a}; std::vector counts{16, 20, 40, 56, 60, 64}; // There aren't that many possible combinations here so it's reasonable to // have no hash collisions. for(AMDGPU::Processor p1 : processors) for(AMDGPU::Processor p2 : processors) for(int c1 : counts) for(int c2 : counts) { AMDGPU g1(p1, c1, 0, "g1"); AMDGPU g2(p2, c2, 0, "g2"); if(p1 != p2 || c1 != c2) { EXPECT_NE(std::hash()(g1), std::hash()(g2)) << g1 << "/" << g2; } else { EXPECT_EQ(std::hash()(g1), std::hash()(g2)) << g1 << "/" << g2; } } } TEST(Hashing, Tuple) { using namespace Tensile; using TwoInts = std::tuple; TwoInts tup(4, 5); EXPECT_EQ(std::hash()(tup), hash_combine(5, 4)); } TEST(Hashing, Tuple2) { using namespace Tensile; using TwoInts = std::tuple; TwoInts tup; size_t h = std::hash()(tup); if(h) // Use the code to quiet the compiler. return; } TEST(CachingLibrary, Simple) { using namespace Tensile; auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); auto Solution2 = std::make_shared(); auto Solution3 = std::make_shared(); Solution0->index = 0; Solution1->index = 1; Solution2->index = 2; Solution3->index = 3; SolutionMap map( {{0, Solution0}, {1, Solution1}, {2, Solution2}, {3, Solution3}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Library2 = std::make_shared(Solution2); auto Library3 = std::make_shared(Solution3); AMDGPU gpu; auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 6, 6, 6, 6, 6, 6, 1.2, false, 1); auto Problem2 = ContractionProblem::GEMM(true, false, 14, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem3 = ContractionProblem::GEMM(true, true, 24, 4, 4, 4, 4, 4, 1.2, false, 1); using Key = std::array; using Table = Matching::DistanceMatchingTable>, std::shared_ptr, Matching::EuclideanDistance>; using Properties = std::vector>>; Properties properties; { auto freeSizeA = std::make_shared(); freeSizeA->index = 0; properties.push_back(freeSizeA); auto freeSizeB = std::make_shared(); freeSizeB->index = 0; properties.push_back(freeSizeB); auto batchSize = std::make_shared(); batchSize->index = 0; properties.push_back(batchSize); auto boundSize = std::make_shared(); boundSize->index = 0; properties.push_back(boundSize); } std::shared_ptr matchingTable = std::make_shared
(properties); using Entry = Matching::MatchingTableEntry>>; std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 1.0}; table.push_back(map0); Entry map1{{6, 6, 1, 6}, Library1, 1.0}; table.push_back(map1); Entry map2{{14, 4, 1, 4}, Library2, 1.0}; table.push_back(map2); Entry map3{{24, 4, 1, 4}, Library3, 1.0}; table.push_back(map3); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem2, gpu)); auto theSolution2 = lib.findBestSolution(Problem2, gpu); EXPECT_EQ(theSolution2, Solution2); auto theSolution2_cached = lib.findSolutionInCache(Problem2, gpu); EXPECT_EQ(theSolution2, theSolution2_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem3, gpu)); auto theSolution3 = lib.findBestSolution(Problem3, gpu); EXPECT_EQ(theSolution3, Solution3); auto theSolution3_cached = lib.findSolutionInCache(Problem3, gpu); EXPECT_EQ(theSolution3, theSolution3_cached); } TEST(CachingLibrary, FlagsDiff) { // This test is to ensure that the CachingLibrary differentiates between // problem 'flags', such as high precision accumulate, kernel language, // deterministic mode and arithmetic unit support. using namespace Tensile; using Key = std::array; using Table = Matching::DistanceMatchingTable>, std::shared_ptr, Matching::EuclideanDistance>; using Properties = std::vector>>; using Entry = Matching::MatchingTableEntry>>; // Default GPU device AMDGPU gpu; // Set up default distance matching properties Properties properties; { auto freeSizeA = std::make_shared(); freeSizeA->index = 0; properties.push_back(freeSizeA); auto freeSizeB = std::make_shared(); freeSizeB->index = 0; properties.push_back(freeSizeB); auto batchSize = std::make_shared(); batchSize->index = 0; properties.push_back(batchSize); auto boundSize = std::make_shared(); boundSize->index = 0; properties.push_back(boundSize); } // Test high precision accumulate caching { auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); Solution0->problemPredicate = std::make_shared(true); Solution1->problemPredicate = std::make_shared(false); Solution0->index = 0; Solution1->index = 1; SolutionMap map( {{Solution0->index, Solution0}, {Solution1->index, Solution1}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); Problem0.setHighPrecisionAccumulate(true); Problem1.setHighPrecisionAccumulate(false); std::shared_ptr
matchingTable = std::make_shared
(properties); std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 1.0}; Entry map1{{4, 4, 1, 4}, Library1, 1.0}; table.push_back(map0); table.push_back(map1); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); } // Test kernel language caching { auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); auto Solution2 = std::make_shared(); Solution0->problemPredicate = std::make_shared( KernelLanguage::Assembly); Solution1->problemPredicate = std::make_shared( KernelLanguage::Source); Solution2->problemPredicate = std::make_shared( KernelLanguage::Any); Solution0->index = 0; Solution1->index = 1; Solution2->index = 2; SolutionMap map({{Solution0->index, Solution0}, {Solution1->index, Solution1}, {Solution2->index, Solution2}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Library2 = std::make_shared(Solution2); auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem2 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); Problem0.setKernelLanguage(KernelLanguage::Assembly); Problem1.setKernelLanguage(KernelLanguage::Source); Problem2.setKernelLanguage(KernelLanguage::Any); std::shared_ptr
matchingTable = std::make_shared
(properties); std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 1.0}; Entry map1{{4, 4, 1, 4}, Library1, 1.0}; Entry map2{{4, 4, 1, 4}, Library2, 2.0}; table.push_back(map0); table.push_back(map1); table.push_back(map2); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem2, gpu)); auto theSolution2 = lib.findBestSolution(Problem2, gpu); EXPECT_EQ(theSolution2, Solution2); auto theSolution2_cached = lib.findSolutionInCache(Problem2, gpu); EXPECT_EQ(theSolution2, theSolution2_cached); } // Test deterministic mode caching { auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); Solution0->problemPredicate = std::make_shared(true); Solution1->problemPredicate = std::make_shared(false); Solution0->index = 0; Solution1->index = 1; SolutionMap map( {{Solution0->index, Solution0}, {Solution1->index, Solution1}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); Problem0.setDeterministicMode(true); Problem1.setDeterministicMode(false); std::shared_ptr
matchingTable = std::make_shared
(properties); std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 1.0}; Entry map1{{4, 4, 1, 4}, Library1, 1.0}; table.push_back(map0); table.push_back(map1); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); } // Test arithmetic unit caching { auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); auto Solution2 = std::make_shared(); Solution0->problemPredicate = std::make_shared( ArithmeticUnit::MFMA); Solution1->problemPredicate = std::make_shared( ArithmeticUnit::VALU); Solution2->problemPredicate = std::make_shared( ArithmeticUnit::Any); Solution0->index = 0; Solution1->index = 1; Solution2->index = 2; SolutionMap map({{Solution0->index, Solution0}, {Solution1->index, Solution1}, {Solution2->index, Solution2}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Library2 = std::make_shared(Solution2); auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem2 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); Problem0.setArithmeticUnit(ArithmeticUnit::MFMA); Problem1.setArithmeticUnit(ArithmeticUnit::VALU); Problem2.setArithmeticUnit(ArithmeticUnit::Any); std::shared_ptr
matchingTable = std::make_shared
(properties); std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 1.0}; Entry map1{{4, 4, 1, 4}, Library1, 1.0}; Entry map2{{4, 4, 1, 4}, Library2, 2.0}; table.push_back(map0); table.push_back(map1); table.push_back(map2); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem2, gpu)); auto theSolution2 = lib.findBestSolution(Problem2, gpu); EXPECT_EQ(theSolution2, Solution2); auto theSolution2_cached = lib.findSolutionInCache(Problem2, gpu); EXPECT_EQ(theSolution2, theSolution2_cached); } // Random combinations, realistic { using And = Predicates::And; using PredicateList = std::vector>>; auto pred_HPA_true = std::make_shared(true); auto pred_HPA_false = std::make_shared(false); auto pred_KL_asm = std::make_shared( KernelLanguage::Assembly); auto pred_KL_src = std::make_shared( KernelLanguage::Source); auto pred_DM_false = std::make_shared(false); auto pred_AUC_mfma = std::make_shared( ArithmeticUnit::MFMA); auto pred_AUC_valu = std::make_shared( ArithmeticUnit::VALU); auto Solution0 = std::make_shared(); auto Solution1 = std::make_shared(); auto Solution2 = std::make_shared(); auto Solution3 = std::make_shared(); auto Solution4 = std::make_shared(); auto Solution5 = std::make_shared(); auto Solution6 = std::make_shared(); auto Solution7 = std::make_shared(); Solution0->problemPredicate = std::make_shared(PredicateList({pred_HPA_true, pred_KL_asm, pred_AUC_valu})); Solution1->problemPredicate = std::make_shared(PredicateList({pred_HPA_true, pred_KL_asm, pred_AUC_mfma})); Solution2->problemPredicate = std::make_shared(PredicateList({pred_HPA_true, pred_KL_src, pred_AUC_valu})); Solution3->problemPredicate = std::make_shared(PredicateList({pred_HPA_true, pred_KL_src, pred_AUC_mfma})); Solution4->problemPredicate = std::make_shared( PredicateList({pred_HPA_true, pred_KL_src, pred_AUC_valu, pred_DM_false})); Solution5->problemPredicate = std::make_shared( PredicateList({pred_HPA_true, pred_KL_src, pred_AUC_mfma, pred_DM_false})); Solution6->problemPredicate = std::make_shared(PredicateList({pred_HPA_true, pred_AUC_valu})); Solution7->problemPredicate = std::make_shared(PredicateList({pred_HPA_false, pred_AUC_valu})); Solution0->index = 0; Solution1->index = 1; Solution2->index = 2; Solution3->index = 3; Solution4->index = 4; Solution5->index = 5; Solution6->index = 6; Solution7->index = 7; SolutionMap map({{Solution0->index, Solution0}, {Solution1->index, Solution1}, {Solution2->index, Solution2}, {Solution3->index, Solution3}, {Solution4->index, Solution4}, {Solution5->index, Solution5}, {Solution6->index, Solution6}, {Solution7->index, Solution7}}); auto Library0 = std::make_shared(Solution0); auto Library1 = std::make_shared(Solution1); auto Library2 = std::make_shared(Solution2); auto Library3 = std::make_shared(Solution3); auto Library4 = std::make_shared(Solution4); auto Library5 = std::make_shared(Solution5); auto Library6 = std::make_shared(Solution6); auto Library7 = std::make_shared(Solution7); auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem2 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem3 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem4 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem5 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem6 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem7 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); Problem0.setHighPrecisionAccumulate(true); Problem0.setKernelLanguage(KernelLanguage::Assembly); Problem0.setArithmeticUnit(ArithmeticUnit::VALU); Problem1.setHighPrecisionAccumulate(true); Problem1.setKernelLanguage(KernelLanguage::Assembly); Problem1.setArithmeticUnit(ArithmeticUnit::MFMA); Problem2.setHighPrecisionAccumulate(true); Problem2.setKernelLanguage(KernelLanguage::Source); Problem2.setArithmeticUnit(ArithmeticUnit::VALU); Problem2.setDeterministicMode(true); Problem3.setHighPrecisionAccumulate(true); Problem3.setKernelLanguage(KernelLanguage::Source); Problem3.setArithmeticUnit(ArithmeticUnit::MFMA); Problem3.setDeterministicMode(true); Problem4.setHighPrecisionAccumulate(true); Problem4.setKernelLanguage(KernelLanguage::Source); Problem4.setArithmeticUnit(ArithmeticUnit::VALU); Problem5.setHighPrecisionAccumulate(true); Problem5.setKernelLanguage(KernelLanguage::Source); Problem5.setArithmeticUnit(ArithmeticUnit::MFMA); Problem6.setHighPrecisionAccumulate(true); Problem6.setArithmeticUnit(ArithmeticUnit::VALU); Problem7.setHighPrecisionAccumulate(false); Problem7.setArithmeticUnit(ArithmeticUnit::VALU); std::shared_ptr
matchingTable = std::make_shared
(properties); std::vector table; { Entry map0{{4, 4, 1, 4}, Library0, 6.0}; Entry map1{{4, 4, 1, 4}, Library1, 1.0}; Entry map2{{4, 4, 1, 4}, Library2, 2.0}; Entry map3{{4, 4, 1, 4}, Library3, 3.0}; Entry map4{{4, 4, 1, 4}, Library4, 4.0}; Entry map5{{4, 4, 1, 4}, Library5, 5.0}; Entry map6{{4, 4, 1, 4}, Library6, 1.0}; Entry map7{{4, 4, 1, 4}, Library7, 2.0}; table.push_back(map0); table.push_back(map1); table.push_back(map2); table.push_back(map3); table.push_back(map4); table.push_back(map5); table.push_back(map6); table.push_back(map7); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem0, gpu)); auto theSolution0 = lib.findBestSolution(Problem0, gpu); EXPECT_EQ(theSolution0, Solution0); auto theSolution0_cached = lib.findSolutionInCache(Problem0, gpu); EXPECT_EQ(theSolution0, theSolution0_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem1, gpu)); auto theSolution1 = lib.findBestSolution(Problem1, gpu); EXPECT_EQ(theSolution1, Solution1); auto theSolution1_cached = lib.findSolutionInCache(Problem1, gpu); EXPECT_EQ(theSolution1, theSolution1_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem2, gpu)); auto theSolution2 = lib.findBestSolution(Problem2, gpu); EXPECT_EQ(theSolution2, Solution2); auto theSolution2_cached = lib.findSolutionInCache(Problem2, gpu); EXPECT_EQ(theSolution2, theSolution2_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem3, gpu)); auto theSolution3 = lib.findBestSolution(Problem3, gpu); EXPECT_EQ(theSolution3, Solution3); auto theSolution3_cached = lib.findSolutionInCache(Problem3, gpu); EXPECT_EQ(theSolution3, theSolution3_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem4, gpu)); auto theSolution4 = lib.findBestSolution(Problem4, gpu); EXPECT_EQ(theSolution4, Solution4); auto theSolution4_cached = lib.findSolutionInCache(Problem4, gpu); EXPECT_EQ(theSolution4, theSolution4_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem5, gpu)); auto theSolution5 = lib.findBestSolution(Problem5, gpu); EXPECT_EQ(theSolution5, Solution5); auto theSolution5_cached = lib.findSolutionInCache(Problem5, gpu); EXPECT_EQ(theSolution5, theSolution5_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem6, gpu)); auto theSolution6 = lib.findBestSolution(Problem6, gpu); EXPECT_EQ(theSolution6, Solution0); // Best match auto theSolution6_cached = lib.findSolutionInCache(Problem6, gpu); EXPECT_EQ(theSolution6, theSolution6_cached); EXPECT_EQ(nullptr, lib.findSolutionInCache(Problem7, gpu)); auto theSolution7 = lib.findBestSolution(Problem7, gpu); EXPECT_EQ(theSolution7, Solution7); auto theSolution7_cached = lib.findSolutionInCache(Problem7, gpu); EXPECT_EQ(theSolution7, theSolution7_cached); } } TEST(CachingLibrary, Insert) { using namespace Tensile; auto SolutionDefault = std::make_shared(); auto SolutionOverride = std::make_shared(); SolutionDefault->index = 0; SolutionOverride->index = 1; SolutionMap map({{0, SolutionDefault}, {1, SolutionOverride}}); auto LibraryDefault = std::make_shared(SolutionDefault); auto LibraryOverride = std::make_shared(SolutionOverride); AMDGPU gpu; auto Problem0 = ContractionProblem::GEMM(false, false, 4, 4, 4, 4, 4, 4, 1.2, false, 1); auto Problem1 = ContractionProblem::GEMM(false, false, 5, 5, 5, 5, 5, 5, 1.2, false, 1); auto Problem2 = ContractionProblem::GEMM(true, false, 6, 6, 6, 6, 6, 6, 1.2, false, 1); auto Problem3 = ContractionProblem::GEMM(true, true, 7, 7, 7, 7, 7, 7, 1.2, false, 1); using Key = std::array; using Table = Matching::DistanceMatchingTable>, std::shared_ptr, Matching::EuclideanDistance>; using Properties = std::vector>>; Properties properties; { auto freeSizeA = std::make_shared(); freeSizeA->index = 0; properties.push_back(freeSizeA); auto freeSizeB = std::make_shared(); freeSizeB->index = 0; properties.push_back(freeSizeB); auto batchSize = std::make_shared(); batchSize->index = 0; properties.push_back(batchSize); auto boundSize = std::make_shared(); boundSize->index = 0; properties.push_back(boundSize); } std::shared_ptr
matchingTable = std::make_shared
(properties); using Entry = Matching::MatchingTableEntry>>; std::vector table; { Entry map0{{4, 4, 1, 4}, LibraryDefault, 1.0}; table.push_back(map0); Entry map1{{1000, 1000, 1, 1000}, LibraryOverride, 1.0}; table.push_back(map1); } matchingTable->table = table; auto subLib = std::make_shared>(); subLib->table = matchingTable; CachingLibrary lib(subLib); // Add before solution cached EXPECT_TRUE(lib.addToOverride(Problem1, gpu, SolutionOverride)); EXPECT_EQ(lib.findBestSolution(Problem0, gpu), SolutionDefault); EXPECT_EQ(lib.findBestSolution(Problem1, gpu), SolutionOverride); EXPECT_EQ(lib.findBestSolution(Problem2, gpu), SolutionDefault); EXPECT_EQ(lib.findBestSolution(Problem3, gpu), SolutionDefault); // Add after solution cached EXPECT_TRUE(lib.addToOverride(Problem3, gpu, SolutionOverride)); EXPECT_FALSE(lib.addToOverride(Problem3, gpu, nullptr)); EXPECT_EQ(lib.findBestSolution(Problem0, gpu), SolutionDefault); EXPECT_EQ(lib.findBestSolution(Problem1, gpu), SolutionOverride); EXPECT_EQ(lib.findBestSolution(Problem2, gpu), SolutionDefault); EXPECT_EQ(lib.findBestSolution(Problem3, gpu), SolutionOverride); } TEST(CachingLibrary, Parsing) { using namespace Tensile; // Non-strided std::vector entries0{"T", "N", "2304", "256", "1", "1729", "1", "1", "1729", "1729", "2304", "f16_r", "f16_r", "f32_r", "752"}; auto probSol = problemFromEntries(entries0); EXPECT_TRUE(probSol.first.transA()); EXPECT_FALSE(probSol.first.transB()); EXPECT_EQ(probSol.first.m(), 2304); EXPECT_EQ(probSol.first.n(), 256); EXPECT_EQ(probSol.first.batchSize(), 1); EXPECT_EQ(probSol.first.k(), 1729); EXPECT_EQ(probSol.first.beta(), 1.0); EXPECT_EQ(probSol.first.inputType(), DataType::Half); EXPECT_EQ(probSol.first.outputType(), DataType::Half); EXPECT_TRUE(probSol.first.HPA()); EXPECT_EQ(probSol.second, 752); // Strided std::vector entries1{"N", "T", "104", "104", "1024", "64", "1", "0", "104", "64", "104", "6656", "6656", "10816", "f32_r", "f32_r", "f32_r", "3976"}; probSol = problemFromEntries(entries1); EXPECT_FALSE(probSol.first.transA()); EXPECT_TRUE(probSol.first.transB()); EXPECT_EQ(probSol.first.m(), 104); EXPECT_EQ(probSol.first.n(), 104); EXPECT_EQ(probSol.first.batchSize(), 1024); EXPECT_EQ(probSol.first.k(), 64); EXPECT_EQ(probSol.first.beta(), 0.0); EXPECT_EQ(probSol.first.inputType(), DataType::Float); EXPECT_EQ(probSol.first.outputType(), DataType::Float); EXPECT_FALSE(probSol.first.HPA()); EXPECT_EQ(probSol.second, 3976); // Bad args std::vector entries2{"N", "T", "104"}; // Too short probSol = problemFromEntries(entries2); EXPECT_EQ(probSol.second, -1); std::vector entries3{"T", "N", "2304", "256", "1", "1729", "1", "1", "1729", "1729", "2304", "f1_r", // bad datatype "f16_r", "f32_r", "752"}; probSol = problemFromEntries(entries3); EXPECT_EQ(probSol.second, -1); std::vector entries4{"T", "N", "b", // bad size "256", "1", "1729", "1", "1", "1729", "1729", "2304", "f16_r", "f16_r", "f32_r", "752"}; probSol = problemFromEntries(entries4); EXPECT_EQ(probSol.second, -1); }