diff options
| author | crueter <crueter@eden-emu.dev> | 2026-08-31 02:58:09 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-08-31 02:58:09 +0200 |
| commit | 7fda6dde73ab9fa1eab40d43c90720a1d9ba054f (patch) | |
| tree | 12753135a33c3002ecb35734ac7c79adc5de85b7 /src/common | |
| parent | 106a61c9434b8d61ab8cde0170f6fcac403b28f1 (diff) | |
[common] Switch to boost::unordered_flat containers (#4326)
Replaces all instances of ankerl's unordered map/set with boost's
`unordered_flat_*` classes. This uses std::hash since boost::hash is
actually a lot slower.
Also adds an abstraction layer in `Common` so future changes are quicker
and easier.
Other implementation details:
- ankerl provided hash specializations for tuple and pair, so those were
ported here
- std::erase_if doesn't work on boost, so just used the ADL'd erase_if
This should be about equal or superior performance as unordered_dense for everything except iteration.
Signed-off-by: crueter <crueter@eden-emu.dev>
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
-------------------
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4326
Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/common/container/unordered_map.h | 16 | ||||
| -rw-r--r-- | src/common/container/unordered_set.h | 16 | ||||
| -rw-r--r-- | src/common/container_hash.h | 33 | ||||
| -rw-r--r-- | src/common/fs/path_util.cpp | 6 | ||||
| -rw-r--r-- | src/common/host_memory.cpp | 4 | ||||
| -rw-r--r-- | src/common/input.h | 4 | ||||
| -rw-r--r-- | src/common/param_package.h | 4 | ||||
| -rw-r--r-- | src/common/x64/xbyak.h | 7 |
9 files changed, 81 insertions, 14 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 10e8cd0cc7..d90190e6a0 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -147,7 +147,8 @@ add_library( cpu_features.cpp cpu_features.h httplib.h - net/net.h net/net.cpp) + net/net.h net/net.cpp + container/unordered_map.h container/unordered_set.h) if(WIN32) target_sources(common PRIVATE windows/timer_resolution.cpp @@ -241,7 +242,7 @@ if (lz4_ADDED) target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib) endif() -target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense) +target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) # Please refer to src/common/demangle.cpp diff --git a/src/common/container/unordered_map.h b/src/common/container/unordered_map.h new file mode 100644 index 0000000000..7d9e49fd80 --- /dev/null +++ b/src/common/container/unordered_map.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/container_hash.h" + +#include <boost/unordered/unordered_flat_map.hpp> + +namespace Common { + +template <class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>, + class Allocator = std::allocator<std::pair<const Key, T>>> +using unordered_map = boost::unordered::unordered_flat_map<Key, T, Hash, Pred, Allocator>; + +} diff --git a/src/common/container/unordered_set.h b/src/common/container/unordered_set.h new file mode 100644 index 0000000000..54d71406a1 --- /dev/null +++ b/src/common/container/unordered_set.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/container_hash.h" + +#include <boost/unordered/unordered_flat_set.hpp> + +namespace Common { + +template <class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>, + class Allocator = std::allocator<Key>> +using unordered_set = boost::unordered::unordered_flat_set<Key, Hash, Pred, Allocator>; + +} diff --git a/src/common/container_hash.h b/src/common/container_hash.h index b6d8e02e26..d7477f7e4f 100644 --- a/src/common/container_hash.h +++ b/src/common/container_hash.h @@ -10,8 +10,11 @@ #include <array> #include <climits> #include <cstdint> +#include <functional> #include <limits> +#include <tuple> #include <type_traits> +#include <utility> #include <vector> namespace Common { @@ -69,10 +72,17 @@ struct HashCombineImpl<64> { } // namespace detail template <typename T> + requires std::is_unsigned_v<T> inline void HashCombine(std::size_t& seed, const T& v) { seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v)); } +template <typename T> + requires (!std::is_unsigned_v<T>) +inline void HashCombine(std::size_t& seed, const T& v) { + seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, std::hash<T>{}(v)); +} + template <typename It> inline std::size_t HashRange(It first, It last) { std::size_t seed = 0; @@ -95,3 +105,26 @@ std::size_t HashValue(const std::vector<T, Allocator>& v) { } } // namespace Common + +namespace std { + +template <typename... Args> +struct hash<std::tuple<Args...>> { + std::size_t operator()(const std::tuple<Args...>& t) const noexcept { + std::size_t seed = 0; + std::apply([&seed](const Args&... args) { (Common::HashCombine(seed, args), ...); }, t); + return seed; + } +}; + +template <class A, class B> +struct hash<std::pair<A, B>> { + std::size_t operator()(const std::pair<A, B>& p) const noexcept { + std::size_t seed = 0; + Common::HashCombine(seed, p.first); + Common::HashCombine(seed, p.second); + return seed; + } +}; + +} // namespace std diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 9adf210ab7..b4c7eae343 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -7,7 +7,7 @@ #include <algorithm> #include <iostream> #include <sstream> -#include <ankerl/unordered_dense.h> +#include "common/container/unordered_map.h" #include "common/assert.h" #include "common/fs/fs.h" @@ -196,8 +196,8 @@ private: SetLegacyPathImpl(legacy_path, new_path); } - ankerl::unordered_dense::map<EdenPath, fs::path> eden_paths; - ankerl::unordered_dense::map<EmuPath, fs::path> legacy_paths; + ::Common::unordered_map<EdenPath, fs::path> eden_paths; + ::Common::unordered_map<EmuPath, fs::path> legacy_paths; }; bool ValidatePath(const fs::path& path) { diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 445d34c444..fa30b2c047 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -7,7 +7,7 @@ #ifdef _WIN32 #include <iterator> -#include <ankerl/unordered_dense.h> +#include "common/container/unordered_map.h" #include <boost/icl/separate_interval_set.hpp> #include <windows.h> #include "common/dynamic_library.h" @@ -391,7 +391,7 @@ private: std::mutex placeholder_mutex; ///< Mutex for placeholders boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders - ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset + ::Common::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset }; #elif defined(__OPENORBIS__) || defined(__managarm__) diff --git a/src/common/input.h b/src/common/input.h index bd9b2d1656..514f93b494 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -9,7 +9,7 @@ #include <functional> #include <memory> #include <string> -#include <ankerl/unordered_dense.h> +#include "common/container/unordered_map.h" #include <utility> #include <vector> #include "common/logging.h" @@ -412,7 +412,7 @@ public: namespace Impl { template <typename InputDeviceType> -using FactoryListType = ankerl::unordered_dense::map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; +using FactoryListType = ::Common::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; template <typename InputDeviceType> struct FactoryList { diff --git a/src/common/param_package.h b/src/common/param_package.h index 0216863a39..5561dc1b7b 100644 --- a/src/common/param_package.h +++ b/src/common/param_package.h @@ -8,14 +8,14 @@ #include <initializer_list> #include <string> -#include <ankerl/unordered_dense.h> +#include "common/container/unordered_map.h" namespace Common { /// A string-based key-value container supporting serializing to and deserializing from a string class ParamPackage { public: - using DataType = ankerl::unordered_dense::map<std::string, std::string>; + using DataType = ::Common::unordered_map<std::string, std::string>; ParamPackage() = default; explicit ParamPackage(const std::string& serialized); diff --git a/src/common/x64/xbyak.h b/src/common/x64/xbyak.h index 5288c5184f..11d3e2b184 100644 --- a/src/common/x64/xbyak.h +++ b/src/common/x64/xbyak.h @@ -22,10 +22,11 @@ #endif // You must ensure this matches with src/common/x64/xbyak.h on root dir -#include <ankerl/unordered_dense.h> +#include "common/container/unordered_map.h" +#include "common/container/unordered_set.h" #include <boost/unordered_map.hpp> -#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set -#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map +#define XBYAK_STD_UNORDERED_SET ::Common::unordered_set +#define XBYAK_STD_UNORDERED_MAP ::Common::unordered_map #define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap #include <xbyak/xbyak.h> #include <xbyak/xbyak_util.h> |
