/******************************************************************************* * * MIT License * * Copyright (C) 2022-2023 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 #include namespace TensileLite { template struct MultiLevelMap { using type = typename MultiLevelMap, Keys...>::type; }; template struct MultiLevelMap { using type = std::unordered_map; }; /** * Thread-safe multi-valued cache. * * Note that due to a quirk with templates, the order of the keys in find() and add() is *opposite* of that in the type. * * e.g. * * CacheMap myCache * myCache.find("foo", 1.4); // great * myCache.find(1.4, "foo"); // error! */ template class CacheMap { using Map = typename MultiLevelMap::type; public: CacheMap(Value const& nullValue) : m_nullValue(nullValue) , m_lookupEfficiency(Debug::Instance().printLookupEfficiency()) , m_lookups(0) , m_hits(0) { } ~CacheMap() { if(m_lookupEfficiency) std::cout << "CacheMap: " << m_hits << "/" << m_lookups << " cache hits" << std::endl; } template Value find(Ks const&... keys) { std::shared_lock lock(m_mutex); auto rv = find_impl(m_map, keys...); if(m_lookupEfficiency) { m_lookups++; if(rv != m_nullValue) m_hits++; } return rv; } template void add(Value const& value, Ks const&... ks) { std::lock_guard lock(m_mutex); add_impl(m_map, value, ks...); } private: template Value find_impl(SubMap const& map, K const& key) { auto iter = map.find(key); if(iter == map.end()) return m_nullValue; return iter->second; } template Value find_impl(SubMap const& map, K const& key, Ks const&... ks) { auto iter = map.find(key); if(iter == map.end()) return m_nullValue; return find_impl(iter->second, ks...); } template void add_impl(SubMap& map, Value const& value, K const& key) { map.insert_or_assign(key, value); } template void add_impl(SubMap& map, Value const& value, K const& key, Ks const&... ks) { add_impl(map[key], value, ks...); } Map m_map; std::shared_timed_mutex m_mutex; Value m_nullValue; bool m_lookupEfficiency; std::atomic m_lookups; std::atomic m_hits; }; template class CachingLibrary : public SolutionLibrary { public: using Library = SolutionLibrary; using Cache = CacheMap, double>, AMDGPU, MyProblem>; using Caches = CacheMap, AMDGPU, MyProblem>; using CachesAllSolsFlag = CacheMap; using CachesGroupedGemm = CacheMap, AMDGPU, std::vector>; CachingLibrary(std::shared_ptr subLibrary) : m_subLibrary(subLibrary) , m_cache(std::make_tuple(nullptr, std::numeric_limits::max())) , m_caches(SolutionVector{}) , m_cachesAllSolutions(false) , m_cachesGroupedGemm(SolutionVector{}) { } virtual std::shared_ptr getSolutionByIndex(MyProblem const& problem, Hardware const& hardware, const int index) const override { return m_subLibrary->getSolutionByIndex(problem, hardware, index); } virtual std::shared_ptr findBestSolution(MyProblem const& problem, Hardware const& hardware, double* fitness = nullptr) const override { try { double cachedFitness = std::numeric_limits::max(); fitness = (fitness) ? fitness : &cachedFitness; auto const& amdgpu = dynamic_cast(hardware); std::shared_ptr solution; std::tie(solution, *fitness) = m_cache.find(problem, amdgpu); if(solution) return solution; solution = m_subLibrary->findBestSolution(problem, hardware, fitness); if(solution) m_cache.add(std::make_tuple(solution, *fitness), problem, amdgpu); return solution; } catch(std::bad_cast const& exc) { return m_subLibrary->findBestSolution(problem, hardware, fitness); } } virtual SolutionSet findAllSolutions(MyProblem const& problem, Hardware const& hardware, SolutionLibrarySearchType searchType = SolutionLibrarySearchType::DEFAULT) const override { return m_subLibrary->findAllSolutions(problem, hardware, searchType); } virtual SolutionSet findAllSolutionsGroupedGemm(std::vector const& problems, Hardware const& hardware, SolutionLibrarySearchType searchType = SolutionLibrarySearchType::DEFAULT) const override { return m_subLibrary->findAllSolutionsGroupedGemm(problems, hardware, searchType); } std::shared_ptr findSolutionInCache(MyProblem const& problem, Hardware const& hardware) const { auto const& amdgpu = dynamic_cast(hardware); return std::get>(m_cache.find(problem, amdgpu)); } virtual std::string type() const override { return "Caching Library"; } virtual std::string description() const override { return "Caching Library"; } std::shared_ptr library() const { return m_subLibrary; } virtual SolutionVector findTopSolutions(MyProblem const& problem, Hardware const& hardware, int numSolutions) const override { try { auto const& amdgpu = dynamic_cast(hardware); SolutionVector solutions; bool cacheAlreadyContainAll; solutions = m_caches.find(problem, amdgpu); cacheAlreadyContainAll = m_cachesAllSolutions.find(problem, amdgpu); // set flag in case of early return lastFindTopRetAll = cacheAlreadyContainAll; if(solutions.size() >= numSolutions || cacheAlreadyContainAll) return solutions; solutions = m_subLibrary->findTopSolutions(problem, hardware, numSolutions); if(solutions.size() != 0) { bool alreadyRetAll = m_subLibrary->lastFindTopAlreadyRetAll(); m_caches.add(solutions, problem, amdgpu); m_cachesAllSolutions.add(alreadyRetAll, problem, amdgpu); // debug // std::cout << "m_cachesAllSolutions.add() with solution.size() = " << solutions.size() // << " and alreadyRetAll: " << (alreadyRetAll? "True" : "False") << std::endl; } // can't reach the requested number, means findTop already done its best lastFindTopRetAll = (solutions.size() < numSolutions); return solutions; } catch(std::bad_cast const& exc) { return m_subLibrary->findTopSolutions(problem, hardware, numSolutions); } // TODO- redundant ?? return m_subLibrary->findTopSolutions(problem, hardware, numSolutions); } virtual bool lastFindTopAlreadyRetAll() const override { return lastFindTopRetAll; } virtual SolutionVector findTopSolutionsGroupedGemm(std::vector const& problems, Hardware const& hardware, int numSolutions) const override { try { auto const& amdgpu = dynamic_cast(hardware); SolutionVector solutions; solutions = m_cachesGroupedGemm.find(problems, amdgpu); if(solutions.size() != 0) return solutions; solutions = m_subLibrary->findTopSolutionsGroupedGemm(problems, hardware, numSolutions); if(solutions.size() != 0) m_cachesGroupedGemm.add(solutions, problems, amdgpu); return solutions; } catch(std::bad_cast const& exc) { return m_subLibrary->findTopSolutionsGroupedGemm(problems, hardware, numSolutions); } return m_subLibrary->findTopSolutionsGroupedGemm(problems, hardware, numSolutions); } private: std::shared_ptr m_subLibrary; mutable Cache m_cache; mutable Caches m_caches; mutable CachesAllSolsFlag m_cachesAllSolutions; mutable CachesGroupedGemm m_cachesGroupedGemm; mutable std::atomic lastFindTopRetAll = false; }; #if 0 struct ContractionCachingLibrary: public CachingLibrary { using Super = CachingLibrary; using Library = typename Super::Library; using Key = typename Super::Key; ContractionCachingLibrary(std::shared_ptr subLibrary) : CachingLibrary(subLibrary) {} }; #endif } // namespace TensileLite