[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>
This commit is contained in:
crueter
2026-08-31 02:58:09 +02:00
parent 106a61c943
commit 7fda6dde73
118 changed files with 348 additions and 340 deletions
-1
View File
@@ -120,7 +120,6 @@ if (NOT Boost_FOUND)
endif()
find_package(fmt 8 CONFIG)
find_package(unordered_dense REQUIRED)
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
find_package(oaknut 2.0.1 CONFIG)
@@ -8,7 +8,6 @@ if (NOT @BUILD_SHARED_LIBS@)
find_dependency(Boost 1.57)
find_dependency(fmt 9)
find_dependency(mcl 0.1.12 EXACT)
find_dependency(unordered_dense)
if ("arm64" IN_LIST ARCHITECTURE)
find_dependency(oaknut 2.0.1)
-1
View File
@@ -387,7 +387,6 @@ set_target_properties(dynarmic PROPERTIES
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
target_link_libraries(dynarmic PRIVATE unordered_dense::unordered_dense)
target_link_libraries(dynarmic PUBLIC fmt::fmt common)
if (BOOST_NO_HEADERS)
@@ -75,7 +75,7 @@ CodePtr AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) {
return block_info.entry_point;
}
void AddressSpace::InvalidateBasicBlocks(const ankerl::unordered_dense::set<IR::LocationDescriptor>& descriptors) {
void AddressSpace::InvalidateBasicBlocks(const ::Common::unordered_set<IR::LocationDescriptor>& descriptors) {
UnprotectCodeMemory();
for (const auto& descriptor : descriptors) {
@@ -14,7 +14,8 @@
#include "common/common_types.h"
#include <oaknut/code_block.hpp>
#include <oaknut/oaknut.hpp>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include "dynarmic/backend/arm64/emit_arm64.h"
#include "dynarmic/backend/arm64/fastmem.h"
@@ -41,7 +42,7 @@ public:
CodePtr GetOrEmit(IR::LocationDescriptor descriptor);
void InvalidateBasicBlocks(const ankerl::unordered_dense::set<IR::LocationDescriptor>& descriptors);
void InvalidateBasicBlocks(const ::Common::unordered_set<IR::LocationDescriptor>& descriptors);
void ClearCache();
protected:
@@ -76,9 +77,9 @@ protected:
// A IR::LocationDescriptor will have one current CodePtr.
// However, there can be multiple other CodePtrs which are older, previously invalidated blocks.
std::map<CodePtr, IR::LocationDescriptor> reverse_block_entries;
ankerl::unordered_dense::map<IR::LocationDescriptor, CodePtr> block_entries;
ankerl::unordered_dense::map<CodePtr, EmittedBlockInfo> block_infos;
ankerl::unordered_dense::map<IR::LocationDescriptor, ankerl::unordered_dense::set<CodePtr>> block_references;
::Common::unordered_map<IR::LocationDescriptor, CodePtr> block_entries;
::Common::unordered_map<CodePtr, EmittedBlockInfo> block_infos;
::Common::unordered_map<IR::LocationDescriptor, ::Common::unordered_set<CodePtr>> block_references;
ExceptionHandler exception_handler;
FastmemManager fastmem_manager;
@@ -14,7 +14,7 @@
#include <vector>
#include "common/common_types.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "dynarmic/backend/arm64/fastmem.h"
#include "dynarmic/interface/A32/coprocessor.h"
@@ -105,8 +105,8 @@ struct EmittedBlockInfo {
CodePtr entry_point;
std::size_t size;
std::vector<Relocation> relocations;
ankerl::unordered_dense::map<IR::LocationDescriptor, std::vector<BlockRelocation>> block_relocations;
ankerl::unordered_dense::map<std::ptrdiff_t, FastmemPatchInfo> fastmem_patch_info;
::Common::unordered_map<IR::LocationDescriptor, std::vector<BlockRelocation>> block_relocations;
::Common::unordered_map<std::ptrdiff_t, FastmemPatchInfo> fastmem_patch_info;
};
struct EmitConfig {
@@ -10,7 +10,7 @@
#include <cstddef>
#include <tuple>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_set.h"
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
@@ -59,7 +59,7 @@ public:
private:
ExceptionHandler& exception_handler;
ankerl::unordered_dense::set<DoNotFastmemMarker, DoNotFastmemMarkerHash> do_not_fastmem;
::Common::unordered_set<DoNotFastmemMarker, DoNotFastmemMarkerHash> do_not_fastmem;
};
} // namespace Dynarmic::Backend::Arm64
@@ -18,7 +18,8 @@
#include "common/common_types.h"
#include "dynarmic/mcl/is_instance_of_template.hpp"
#include <oaknut/oaknut.hpp>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include "dynarmic/backend/arm64/stack_layout.h"
#include "dynarmic/ir/cond.h"
@@ -336,7 +337,7 @@ private:
std::array<HostLocInfo, SpillCount> spills;
mutable std::size_t alloc_candidate_index = 0;
ankerl::unordered_dense::set<const IR::Inst*> defined_insts;
::Common::unordered_set<const IR::Inst*> defined_insts;
};
template<typename T>
@@ -11,13 +11,13 @@
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#include "common/common_types.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_set.h"
namespace Dynarmic::Backend {
template<typename P>
void BlockRangeInformation<P>::AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location) {
block_ranges.add(std::make_pair(range, ankerl::unordered_dense::set<IR::LocationDescriptor>{location}));
block_ranges.add(std::make_pair(range, ::Common::unordered_set<IR::LocationDescriptor>{location}));
}
template<typename P>
@@ -26,8 +26,8 @@ void BlockRangeInformation<P>::ClearCache() {
}
template<typename P>
ankerl::unordered_dense::set<IR::LocationDescriptor> BlockRangeInformation<P>::InvalidateRanges(const boost::icl::interval_set<P>& ranges) {
ankerl::unordered_dense::set<IR::LocationDescriptor> erase_locations;
::Common::unordered_set<IR::LocationDescriptor> BlockRangeInformation<P>::InvalidateRanges(const boost::icl::interval_set<P>& ranges) {
::Common::unordered_set<IR::LocationDescriptor> erase_locations;
for (auto invalidate_interval : ranges) {
auto pair = block_ranges.equal_range(invalidate_interval);
for (auto it = pair.first; it != pair.second; ++it)
@@ -12,7 +12,8 @@
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include "dynarmic/ir/location_descriptor.h"
@@ -23,8 +24,8 @@ class BlockRangeInformation {
public:
void AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location);
void ClearCache();
ankerl::unordered_dense::set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<P>& ranges);
boost::icl::interval_map<P, ankerl::unordered_dense::set<IR::LocationDescriptor>> block_ranges;
::Common::unordered_set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<P>& ranges);
boost::icl::interval_map<P, ::Common::unordered_set<IR::LocationDescriptor>> block_ranges;
};
} // namespace Dynarmic::Backend
@@ -17,7 +17,7 @@
#include <optional>
#include <shared_mutex>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include <fmt/format.h>
#include <sys/mman.h>
@@ -55,7 +55,7 @@ class SigHandler {
});
}
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
::Common::unordered_map<u64, CodeBlockInfo> code_block_infos;
std::shared_mutex code_block_infos_mutex;
struct sigaction old_sa_segv;
struct sigaction old_sa_bus;
@@ -4,7 +4,7 @@
#pragma once
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "dynarmic/backend/loongarch64/code_block.h"
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
@@ -53,8 +53,8 @@ private:
const A32::UserConfig conf;
CodeBlock cb;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
::Common::unordered_map<u64, CodePtr> block_entries;
::Common::unordered_map<u64, EmittedBlockInfo> block_infos;
public:
struct PreludeInfo {
@@ -9,7 +9,7 @@
#include <vector>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/assert.h"
#include "common/common_types.h"
@@ -9,7 +9,7 @@
#pragma once
#include <biscuit/assembler.hpp>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "dynarmic/backend/riscv64/code_block.h"
#include "dynarmic/backend/riscv64/emit_riscv64.h"
@@ -74,8 +74,8 @@ private:
CodeBlock cb;
biscuit::Assembler as;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
::Common::unordered_map<u64, CodePtr> block_entries;
::Common::unordered_map<u64, EmittedBlockInfo> block_infos;
struct PreludeInfo {
CodePtr end_of_prelude;
@@ -19,7 +19,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/mcl/is_instance_of_template.hpp"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "dynarmic/backend/riscv64/stack_layout.h"
#include "dynarmic/ir/cond.h"
@@ -13,7 +13,8 @@
#include <set>
#include <tuple>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include "dynarmic/backend/block_range_information.h"
#include "dynarmic/backend/x64/a32_jitstate.h"
@@ -126,11 +127,11 @@ public:
RegAlloc reg_alloc; //reusable reg alloc
BlockRangeInformation<u32> block_ranges;
std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table;
ankerl::unordered_dense::map<u64, FastmemPatchInfo> fastmem_patch_info;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
ankerl::unordered_dense::set<DoNotFastmemMarker> do_not_fastmem;
::Common::unordered_map<u64, FastmemPatchInfo> fastmem_patch_info;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
::Common::unordered_set<DoNotFastmemMarker> do_not_fastmem;
boost::container::stable_vector<Xbyak::Label> shared_labels;
void (*memory_read_128)() = nullptr; // Dummy
void (*memory_write_128)() = nullptr; // Dummy
@@ -12,7 +12,8 @@
#include <map>
#include <optional>
#include <tuple>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include <boost/container/static_vector.hpp>
#include "dynarmic/backend/block_range_information.h"
@@ -122,11 +123,11 @@ public:
RegAlloc reg_alloc; //reusable reg alloc
BlockRangeInformation<u64> block_ranges;
std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table;
ankerl::unordered_dense::map<u64, FastmemPatchInfo> fastmem_patch_info;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
ankerl::unordered_dense::set<DoNotFastmemMarker> do_not_fastmem;
::Common::unordered_map<u64, FastmemPatchInfo> fastmem_patch_info;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
::Common::unordered_set<DoNotFastmemMarker> do_not_fastmem;
boost::container::stable_vector<Xbyak::Label> shared_labels;
const void* terminal_handler_pop_rsb_hint = nullptr;
const void* terminal_handler_fast_dispatch_hint = nullptr;
@@ -14,7 +14,7 @@
#include <utility>
#include "common/common_types.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic::Backend::X64 {
@@ -43,7 +43,7 @@ private:
}
};
ankerl::unordered_dense::map<ConstantT, void*, ConstantHash> constant_info;
::Common::unordered_map<ConstantT, void*, ConstantHash> constant_info;
std::span<ConstantT> pool;
std::size_t insertion_point;
};
@@ -14,7 +14,8 @@
#include <boost/variant/detail/apply_visitor_binary.hpp>
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/backend/x64/nzcv_util.h"
@@ -396,7 +397,7 @@ void EmitX64::ClearCache() {
PerfMapClear();
}
void EmitX64::InvalidateBasicBlocks(const ankerl::unordered_dense::set<IR::LocationDescriptor>& locations) {
void EmitX64::InvalidateBasicBlocks(const ::Common::unordered_set<IR::LocationDescriptor>& locations) {
code.EnableWriting();
for (const auto& descriptor : locations) {
if (auto const it = block_descriptors.find(descriptor); it != block_descriptors.end()) {
@@ -16,7 +16,8 @@
#include <type_traits>
#include <vector>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/container/unordered_set.h"
#include <boost/container/stable_vector.hpp>
#include <boost/container/small_vector.hpp>
@@ -88,7 +89,7 @@ public:
virtual void ClearCache();
/// Invalidates a selection of basic blocks.
void InvalidateBasicBlocks(const ankerl::unordered_dense::set<IR::LocationDescriptor>& locations);
void InvalidateBasicBlocks(const ::Common::unordered_set<IR::LocationDescriptor>& locations);
//protected:
// Microinstruction emitters
@@ -131,8 +132,8 @@ public:
// State
BlockOfCode& code;
ExceptionHandler exception_handler;
ankerl::unordered_dense::map<IR::LocationDescriptor, BlockDescriptor> block_descriptors;
ankerl::unordered_dense::map<IR::LocationDescriptor, PatchInformation> patch_information;
::Common::unordered_map<IR::LocationDescriptor, BlockDescriptor> block_descriptors;
::Common::unordered_map<IR::LocationDescriptor, PatchInformation> patch_information;
// We need materialized protected members
friend class A64EmitX64;
@@ -4,10 +4,11 @@
#pragma once
// 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>
+2 -2
View File
@@ -11,7 +11,7 @@
#include <map>
#include <bit>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "boost/container/small_vector.hpp"
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
@@ -1433,7 +1433,7 @@ static void VerificationPass(const IR::Block& block) {
ASSERT(IR::AreTypesCompatible(t1, t2));
}
}
ankerl::unordered_dense::map<IR::Inst*, size_t> actual_uses;
::Common::unordered_map<IR::Inst*, size_t> actual_uses;
for (auto const& inst : block.instructions) {
for (size_t i = 0; i < inst.NumArgs(); i++)
if (IR::Value const arg = inst.GetArg(i); !arg.IsImmediate())
+2 -2
View File
@@ -8,7 +8,7 @@
#include <array>
#include <exception>
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include <catch2/catch_test_macros.hpp>
#include "common/common_types.h"
@@ -23,7 +23,7 @@ namespace {
class MyEnvironment final : public A64::UserCallbacks {
public:
u64 ticks_left = 0;
ankerl::unordered_dense::map<u64, u8> memory{};
::Common::unordered_map<u64, u8> memory{};
u8 MemoryRead8(u64 vaddr) override {
return memory[vaddr];
+2 -2
View File
@@ -8,7 +8,7 @@
#pragma once
#include <ankerl/unordered_dense.h>
#include "common/container/unordered_map.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/interface/A64/a64.h"
@@ -17,7 +17,7 @@ using Vector = Dynarmic::A64::Vector;
class A64TestEnv : public Dynarmic::A64::UserCallbacks {
public:
ankerl::unordered_dense::map<u64, u8> modified_memory;
::Common::unordered_map<u64, u8> modified_memory;
std::vector<u32> code_mem;
u64 ticks_left = 0;
u64 code_mem_start_address = 0;
+5 -5
View File
@@ -24,7 +24,7 @@ add_executable(dynarmic_tests
A64/real_world.cpp
A64/testenv.h
)
target_link_libraries(dynarmic_tests PRIVATE merry::oaknut unordered_dense::unordered_dense)
target_link_libraries(dynarmic_tests PRIVATE merry::oaknut)
if (DYNARMIC_TESTS_USE_UNICORN)
target_link_libraries(dynarmic_tests PRIVATE Unicorn::Unicorn)
@@ -72,7 +72,7 @@ add_executable(dynarmic_print_info
print_info.cpp
)
create_target_directory_groups(dynarmic_print_info)
target_link_libraries(dynarmic_print_info PRIVATE dynarmic fmt::fmt unordered_dense::unordered_dense)
target_link_libraries(dynarmic_print_info PRIVATE dynarmic fmt::fmt)
if (BOOST_NO_HEADERS)
target_link_libraries(dynarmic_print_info PRIVATE Boost::variant Boost::icl Boost::pool)
else()
@@ -93,7 +93,7 @@ add_executable(dynarmic_test_generator
create_target_directory_groups(dynarmic_test_generator)
target_link_libraries(dynarmic_test_generator PRIVATE dynarmic fmt::fmt unordered_dense::unordered_dense)
target_link_libraries(dynarmic_test_generator PRIVATE dynarmic fmt::fmt)
if (BOOST_NO_HEADERS)
target_link_libraries(dynarmic_test_generator PRIVATE Boost::variant Boost::icl Boost::pool)
else()
@@ -110,7 +110,7 @@ add_executable(dynarmic_test_reader
test_reader.cpp
)
create_target_directory_groups(dynarmic_test_reader)
target_link_libraries(dynarmic_test_reader PRIVATE dynarmic fmt::fmt unordered_dense::unordered_dense)
target_link_libraries(dynarmic_test_reader PRIVATE dynarmic fmt::fmt)
if (BOOST_NO_HEADERS)
target_link_libraries(dynarmic_test_reader PRIVATE Boost::variant Boost::icl Boost::pool)
else()
@@ -123,7 +123,7 @@ target_compile_definitions(dynarmic_test_reader PRIVATE FMT_USE_USER_DEFINED_LIT
#
create_target_directory_groups(dynarmic_tests)
target_link_libraries(dynarmic_tests PRIVATE dynarmic Catch2::Catch2WithMain fmt::fmt unordered_dense::unordered_dense)
target_link_libraries(dynarmic_tests PRIVATE dynarmic Catch2::Catch2WithMain fmt::fmt)
if (BOOST_NO_HEADERS)
target_link_libraries(dynarmic_tests PRIVATE Boost::variant Boost::icl Boost::pool)
else()