diff --git a/docs/dev/env_vars.rst b/docs/dev/env_vars.rst index 3aa29e84809..8c7ff31b9c2 100644 --- a/docs/dev/env_vars.rst +++ b/docs/dev/env_vars.rst @@ -246,6 +246,12 @@ Set to "1" to print benchmarking trace. Set to "2" to print detailed benchmarking trace. Set to "3" to print compiled traces. +.. envvar:: MIGRAPHX_PROBLEM_CACHE + +Set to path to json file to load and save problem cache. +This will load the json file into the problem cache if it exists, and when +compilation finishes it will save the problem cache. + MLIR vars ------------- diff --git a/docs/sphinx/requirements.txt b/docs/sphinx/requirements.txt index 96ee05fb107..a782d983a07 100644 --- a/docs/sphinx/requirements.txt +++ b/docs/sphinx/requirements.txt @@ -1,3 +1,27 @@ +##################################################################################### +# The MIT License (MIT) +# +# Copyright (c) 2015-2024 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. +# +##################################################################################### # # This file is autogenerated by pip-compile with Python 3.10 # by the following command: diff --git a/src/file_buffer.cpp b/src/file_buffer.cpp index d1735d016a6..dabd1e8829d 100644 --- a/src/file_buffer.cpp +++ b/src/file_buffer.cpp @@ -65,6 +65,11 @@ std::string read_string(const fs::path& filename) return generic_read_file(filename); } +void write_string(const fs::path& filename, const std::string& buffer) +{ + write_buffer(filename, buffer.data(), buffer.size()); +} + void write_buffer(const fs::path& filename, const char* buffer, std::size_t size) { std::ofstream os(filename, std::ios::out | std::ios::binary); diff --git a/src/include/migraphx/file_buffer.hpp b/src/include/migraphx/file_buffer.hpp index 042331e9041..ee257cc8f99 100644 --- a/src/include/migraphx/file_buffer.hpp +++ b/src/include/migraphx/file_buffer.hpp @@ -36,6 +36,7 @@ MIGRAPHX_EXPORT std::vector read_buffer(const fs::path& filename, size_t offset = 0, size_t nbytes = 0); MIGRAPHX_EXPORT std::string read_string(const fs::path& filename); +MIGRAPHX_EXPORT void write_string(const fs::path& filename, const std::string& buffer); MIGRAPHX_EXPORT void write_buffer(const fs::path& filename, const char* buffer, std::size_t size); MIGRAPHX_EXPORT void write_buffer(const fs::path& filename, const std::vector& buffer); diff --git a/src/include/migraphx/serialize.hpp b/src/include/migraphx/serialize.hpp index 5218d0617ea..b3525e41f23 100644 --- a/src/include/migraphx/serialize.hpp +++ b/src/include/migraphx/serialize.hpp @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2015-2024 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 @@ -144,6 +144,12 @@ auto to_value_impl(rank<12>, const T& x) return v; } +template {})> +value to_value_impl(rank<13>, const T& x) +{ + return x; +} + template {})> void from_value_impl(rank<0>, const value& v, T& x) { @@ -191,9 +197,28 @@ template auto from_value_impl(rank<4>, const value& v, T& x) -> decltype(x.insert(*x.begin()), std::declval(), void()) { - x.clear(); - for(auto&& e : v) - x.emplace(e.get_key(), from_value(e)); + if(v.is_object()) + { + x.clear(); + for(auto&& e : v) + x.emplace(from_value(e.get_key()), + from_value(e)); + } + else if(v.is_array()) + { + x.clear(); + for(auto&& e : v) + { + if(e.size() != 2) + MIGRAPHX_THROW("Expected a pair"); + x.emplace(from_value(e[0]), + from_value(e[1])); + } + } + else + { + MIGRAPHX_THROW("Expected object or array"); + } } template {})> @@ -233,18 +258,24 @@ auto from_value_impl(rank<10>, const value& v, T& x) -> decltype(migraphx_from_v migraphx_from_value(v, x); } +template {})> +void from_value_impl(rank<11>, const value& v, T& x) +{ + x = v; +} + } // namespace detail template value to_value(const T& x) { - return detail::to_value_impl(rank<12>{}, x); + return detail::to_value_impl(rank<13>{}, x); } template void from_value(const value& v, T& x) { - detail::from_value_impl(rank<10>{}, v, x); + detail::from_value_impl(rank<11>{}, v, x); } } // namespace MIGRAPHX_INLINE_NS diff --git a/src/targets/gpu/include/migraphx/gpu/context.hpp b/src/targets/gpu/include/migraphx/gpu/context.hpp index 225c2d48356..7e06c4db2fc 100644 --- a/src/targets/gpu/include/migraphx/gpu/context.hpp +++ b/src/targets/gpu/include/migraphx/gpu/context.hpp @@ -210,10 +210,25 @@ struct hip_device struct context { + struct auto_save_problem_cache : problem_cache + { + auto_save_problem_cache() : problem_cache{} {} + + bool auto_save = false; + + auto_save_problem_cache(const auto_save_problem_cache&) = delete; + auto_save_problem_cache& operator=(const auto_save_problem_cache&) = delete; + virtual ~auto_save_problem_cache() + { + if(auto_save) + this->save(); + } + }; context(std::size_t device_id = 0, std::size_t n = value_of(MIGRAPHX_NSTREAMS{}, 1)) : current_device(std::make_shared(device_id, n)), begin_event(create_event()), - finish_event(create_event()) + finish_event(create_event()), + pc(std::make_shared()) { } @@ -334,7 +349,12 @@ struct context return result; } - problem_cache& get_problem_cache() { return pc; } + problem_cache& get_problem_cache() { return *pc; } + void load_problem_cache() + { + pc->load(); + pc->auto_save = true; + } private: // TODO: Make this a vector to support multiple devices @@ -348,7 +368,7 @@ struct context // for stream syncronization shared begin_event = nullptr; shared finish_event = nullptr; - problem_cache pc{}; + std::shared_ptr pc = nullptr; }; inline void migraphx_to_value(value& v, const context& ctx) { v = ctx.to_value(); } diff --git a/src/targets/gpu/include/migraphx/gpu/problem_cache.hpp b/src/targets/gpu/include/migraphx/gpu/problem_cache.hpp index 182ea16fdd0..d70e0687bd5 100644 --- a/src/targets/gpu/include/migraphx/gpu/problem_cache.hpp +++ b/src/targets/gpu/include/migraphx/gpu/problem_cache.hpp @@ -28,16 +28,19 @@ #include #include #include +#include namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { namespace gpu { -struct problem_cache +struct MIGRAPHX_GPU_EXPORT problem_cache { bool has(const std::string& name, const value& problem) const; void insert(const std::string& name, const value& problem, const value& solution); void mark(const std::string& name, const value& problem); optional get(const std::string& name, const value& problem) const; + void load(); + void save() const; std::unordered_map cache; }; diff --git a/src/targets/gpu/problem_cache.cpp b/src/targets/gpu/problem_cache.cpp index 24454f29b94..8eb25f3b862 100644 --- a/src/targets/gpu/problem_cache.cpp +++ b/src/targets/gpu/problem_cache.cpp @@ -24,11 +24,38 @@ */ #include #include +#include +#include +#include +#include +#include namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { namespace gpu { +MIGRAPHX_DECLARE_ENV_VAR(MIGRAPHX_PROBLEM_CACHE) + +void problem_cache::load() +{ + auto pc_path = string_value_of(MIGRAPHX_PROBLEM_CACHE{}); + if(pc_path.empty()) + return; + if(not fs::exists(pc_path)) + { + std::cout << "Problem cache not found. Creating new file.\n"; + return; + } + from_value(from_json_string(read_string(pc_path)), cache); +} +void problem_cache::save() const +{ + auto pc_path = string_value_of(MIGRAPHX_PROBLEM_CACHE{}); + if(pc_path.empty()) + return; + write_string(pc_path, to_pretty_json_string(to_value(cache))); +} + static value create_key(const std::string& name, const value& problem) { return {{"name", name}, {"problem", problem}}; diff --git a/src/targets/gpu/target.cpp b/src/targets/gpu/target.cpp index 9db5ad562b1..a6a04bd9be4 100644 --- a/src/targets/gpu/target.cpp +++ b/src/targets/gpu/target.cpp @@ -86,6 +86,7 @@ std::vector target::get_passes(migraphx::context& gctx, const compile_opti { auto& ctx = any_cast(gctx); ctx.set_exhaustive_tune_flag(options.exhaustive_tune); + ctx.load_problem_cache(); std::set unsupported_types(shape::types().begin(), shape::types().end()); unsupported_types.erase(shape::type_t::float_type); unsupported_types.erase(shape::type_t::fp8e4m3fnuz_type); diff --git a/test/include/test.hpp b/test/include/test.hpp index ed9bcd77f5a..b8740e82ddb 100644 --- a/test/include/test.hpp +++ b/test/include/test.hpp @@ -120,6 +120,15 @@ inline Stream& operator<<(Stream& s, std::nullptr_t) return s; } +template +inline Stream& operator<<(Stream& s, const std::pair& p) +{ + s << "{"; + s << p.first << ", " << p.second; + s << "}"; + return s; +} + template {}>::type> diff --git a/test/serialize_test.cpp b/test/serialize_test.cpp index d89f144d3a2..48e0cd28695 100644 --- a/test/serialize_test.cpp +++ b/test/serialize_test.cpp @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2015-2024 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 @@ -26,6 +26,8 @@ #include #include +#include +#include struct empty_type { @@ -145,6 +147,42 @@ TEST_CASE(serialize_optional) EXPECT(v.to() == 2); } +TEST_CASE(serialize_map) +{ + std::map m = {{1, 1}, {2, 4}, {3, 9}}; + migraphx::value v = migraphx::to_value(m); + EXPECT(v.is_array()); + EXPECT(m == migraphx::from_value>(v)); +} + +TEST_CASE(serialize_invalid_map1) +{ + migraphx::value v = {{1, 1}, {2, 4}, {3, 9, 27}}; + EXPECT(test::throws([&] { migraphx::from_value>(v); })); +} + +TEST_CASE(serialize_invalid_map2) +{ + migraphx::value v = 2; + EXPECT(test::throws([&] { migraphx::from_value>(v); })); +} + +TEST_CASE(serialize_struct_to_map) +{ + migraphx::value v = migraphx::to_value(reflectable_type{}); + EXPECT(v.is_object()); + std::map m; + migraphx::from_value(v, m); + EXPECT(m.size() == v.size()); +} + +TEST_CASE(serialize_vector_value) +{ + std::vector x = {{1}, {2}}; + migraphx::value v = migraphx::to_value(x); + EXPECT(x == migraphx::from_value>(v)); +} + TEST_CASE(from_value_binary) { std::vector data(10);