mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-31 18:11:31 +02:00
Add caching to Config::Info
The goal of this change is to make Config::Get(const Info<T>&) fast so that we can use it in hot paths.
This commit is contained in:
parent
633ab2dd7c
commit
d8744e6db8
@ -86,7 +86,7 @@ template <typename T>
|
||||
static void Set(jint layer, const Config::Location& location, T value)
|
||||
{
|
||||
GetLayer(layer, location)->Set(location, value);
|
||||
Config::InvokeConfigChangedCallbacks();
|
||||
Config::OnConfigChanged();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@ -17,6 +18,7 @@ using Layers = std::map<LayerType, std::shared_ptr<Layer>>;
|
||||
static Layers s_layers;
|
||||
static std::list<ConfigChangedCallback> s_callbacks;
|
||||
static u32 s_callback_guards = 0;
|
||||
static std::atomic<u64> s_config_version = 0;
|
||||
|
||||
static std::shared_mutex s_layers_rw_lock;
|
||||
|
||||
@ -31,7 +33,7 @@ static void AddLayerInternal(std::shared_ptr<Layer> layer)
|
||||
const Config::LayerType layer_type = layer->GetLayer();
|
||||
s_layers.insert_or_assign(layer_type, std::move(layer));
|
||||
}
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader)
|
||||
@ -59,7 +61,7 @@ void RemoveLayer(LayerType layer)
|
||||
|
||||
s_layers.erase(layer);
|
||||
}
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
void AddConfigChangedCallback(ConfigChangedCallback func)
|
||||
@ -67,15 +69,22 @@ void AddConfigChangedCallback(ConfigChangedCallback func)
|
||||
s_callbacks.emplace_back(std::move(func));
|
||||
}
|
||||
|
||||
void InvokeConfigChangedCallbacks()
|
||||
void OnConfigChanged()
|
||||
{
|
||||
if (s_callback_guards)
|
||||
return;
|
||||
|
||||
s_config_version.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
for (const auto& callback : s_callbacks)
|
||||
callback();
|
||||
}
|
||||
|
||||
u64 GetConfigVersion()
|
||||
{
|
||||
return s_config_version.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Explicit load and save of layers
|
||||
void Load()
|
||||
{
|
||||
@ -85,7 +94,7 @@ void Load()
|
||||
for (auto& layer : s_layers)
|
||||
layer.second->Load();
|
||||
}
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
void Save()
|
||||
@ -96,7 +105,7 @@ void Save()
|
||||
for (auto& layer : s_layers)
|
||||
layer.second->Save();
|
||||
}
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
void Init()
|
||||
@ -207,7 +216,7 @@ ConfigChangeCallbackGuard::~ConfigChangeCallbackGuard()
|
||||
if (--s_callback_guards)
|
||||
return;
|
||||
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
|
@ -24,7 +24,10 @@ std::shared_ptr<Layer> GetLayer(LayerType layer);
|
||||
void RemoveLayer(LayerType layer);
|
||||
|
||||
void AddConfigChangedCallback(ConfigChangedCallback func);
|
||||
void InvokeConfigChangedCallbacks();
|
||||
void OnConfigChanged();
|
||||
|
||||
// Returns the number of times the config has changed in the current execution of the program
|
||||
u64 GetConfigVersion();
|
||||
|
||||
// Explicit load and save of layers
|
||||
void Load();
|
||||
@ -51,6 +54,23 @@ T Get(LayerType layer, const Info<T>& info)
|
||||
|
||||
template <typename T>
|
||||
T Get(const Info<T>& info)
|
||||
{
|
||||
CachedValue<T> cached = info.GetCachedValue();
|
||||
const u64 config_version = GetConfigVersion();
|
||||
|
||||
if (cached.config_version < config_version)
|
||||
{
|
||||
cached.value = GetUncached(info);
|
||||
cached.config_version = config_version;
|
||||
|
||||
info.SetCachedValue(cached);
|
||||
}
|
||||
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetUncached(const Info<T>& info)
|
||||
{
|
||||
const std::optional<std::string> str = GetAsString(info.GetLocation());
|
||||
if (!str)
|
||||
@ -75,7 +95,7 @@ template <typename T>
|
||||
void Set(LayerType layer, const Info<T>& info, const std::common_type_t<T>& value)
|
||||
{
|
||||
GetLayer(layer)->Set(info, value);
|
||||
InvokeConfigChangedCallbacks();
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -99,7 +119,7 @@ void SetBaseOrCurrent(const Info<T>& info, const std::common_type_t<T>& value)
|
||||
Set<T>(LayerType::CurrentRun, info, value);
|
||||
}
|
||||
|
||||
// Used to defer InvokeConfigChangedCallbacks until after the completion of many config changes.
|
||||
// Used to defer OnConfigChanged until after the completion of many config changes.
|
||||
class ConfigChangeCallbackGuard
|
||||
{
|
||||
public:
|
||||
|
@ -4,9 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/Enums.h"
|
||||
|
||||
namespace Config
|
||||
@ -29,30 +33,93 @@ struct Location
|
||||
bool operator<(const Location& other) const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CachedValue
|
||||
{
|
||||
T value;
|
||||
u64 config_version;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Info
|
||||
{
|
||||
public:
|
||||
constexpr Info(const Location& location, const T& default_value)
|
||||
: m_location{location}, m_default_value{default_value}
|
||||
: m_location{location}, m_default_value{default_value}, m_cached_value{default_value, 0}
|
||||
{
|
||||
}
|
||||
|
||||
Info(const Info<T>& other) { *this = other; }
|
||||
|
||||
// Not thread-safe
|
||||
Info(Info<T>&& other) { *this = std::move(other); }
|
||||
|
||||
// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
|
||||
// so that enum settings can still easily work with code that doesn't care about the enum values.
|
||||
template <typename Enum,
|
||||
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
|
||||
Info(const Info<Enum>& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Info<T>& operator=(const Info<T>& other)
|
||||
{
|
||||
m_location = other.GetLocation();
|
||||
m_default_value = other.GetDefaultValue();
|
||||
m_cached_value = other.GetCachedValue();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Not thread-safe
|
||||
Info<T>& operator=(Info<T>&& other)
|
||||
{
|
||||
m_location = std::move(other.m_location);
|
||||
m_default_value = std::move(other.m_default_value);
|
||||
m_cached_value = std::move(other.m_cached_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
|
||||
// so that enum settings can still easily work with code that doesn't care about the enum values.
|
||||
template <typename Enum,
|
||||
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
|
||||
constexpr Info(const Info<Enum>& other)
|
||||
: m_location{other.GetLocation()}, m_default_value{static_cast<detail::UnderlyingType<Enum>>(
|
||||
other.GetDefaultValue())}
|
||||
Info<T>& operator=(const Info<Enum>& other)
|
||||
{
|
||||
m_location = other.GetLocation();
|
||||
m_default_value = static_cast<T>(other.GetDefaultValue());
|
||||
m_cached_value = other.template GetCachedValueCasted<T>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const Location& GetLocation() const { return m_location; }
|
||||
constexpr const T& GetDefaultValue() const { return m_default_value; }
|
||||
|
||||
CachedValue<T> GetCachedValue() const
|
||||
{
|
||||
std::shared_lock lock(m_cached_value_mutex);
|
||||
return m_cached_value;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CachedValue<U> GetCachedValueCasted() const
|
||||
{
|
||||
std::shared_lock lock(m_cached_value_mutex);
|
||||
return CachedValue<U>{static_cast<U>(m_cached_value.value), m_cached_value.config_version};
|
||||
}
|
||||
|
||||
void SetCachedValue(const CachedValue<T>& cached_value) const
|
||||
{
|
||||
std::unique_lock lock(m_cached_value_mutex);
|
||||
if (m_cached_value.config_version < cached_value.config_version)
|
||||
m_cached_value = cached_value;
|
||||
}
|
||||
|
||||
private:
|
||||
Location m_location;
|
||||
T m_default_value;
|
||||
|
||||
mutable CachedValue<T> m_cached_value;
|
||||
mutable std::shared_mutex m_cached_value_mutex;
|
||||
};
|
||||
} // namespace Config
|
||||
|
Loading…
Reference in New Issue
Block a user