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)
|
static void Set(jint layer, const Config::Location& location, T value)
|
||||||
{
|
{
|
||||||
GetLayer(layer, location)->Set(location, value);
|
GetLayer(layer, location)->Set(location, value);
|
||||||
Config::InvokeConfigChangedCallbacks();
|
Config::OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -17,6 +18,7 @@ using Layers = std::map<LayerType, std::shared_ptr<Layer>>;
|
||||||
static Layers s_layers;
|
static Layers s_layers;
|
||||||
static std::list<ConfigChangedCallback> s_callbacks;
|
static std::list<ConfigChangedCallback> s_callbacks;
|
||||||
static u32 s_callback_guards = 0;
|
static u32 s_callback_guards = 0;
|
||||||
|
static std::atomic<u64> s_config_version = 0;
|
||||||
|
|
||||||
static std::shared_mutex s_layers_rw_lock;
|
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();
|
const Config::LayerType layer_type = layer->GetLayer();
|
||||||
s_layers.insert_or_assign(layer_type, std::move(layer));
|
s_layers.insert_or_assign(layer_type, std::move(layer));
|
||||||
}
|
}
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader)
|
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader)
|
||||||
|
@ -59,7 +61,7 @@ void RemoveLayer(LayerType layer)
|
||||||
|
|
||||||
s_layers.erase(layer);
|
s_layers.erase(layer);
|
||||||
}
|
}
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddConfigChangedCallback(ConfigChangedCallback func)
|
void AddConfigChangedCallback(ConfigChangedCallback func)
|
||||||
|
@ -67,15 +69,22 @@ void AddConfigChangedCallback(ConfigChangedCallback func)
|
||||||
s_callbacks.emplace_back(std::move(func));
|
s_callbacks.emplace_back(std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvokeConfigChangedCallbacks()
|
void OnConfigChanged()
|
||||||
{
|
{
|
||||||
if (s_callback_guards)
|
if (s_callback_guards)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
s_config_version.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
|
||||||
for (const auto& callback : s_callbacks)
|
for (const auto& callback : s_callbacks)
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 GetConfigVersion()
|
||||||
|
{
|
||||||
|
return s_config_version.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
// Explicit load and save of layers
|
// Explicit load and save of layers
|
||||||
void Load()
|
void Load()
|
||||||
{
|
{
|
||||||
|
@ -85,7 +94,7 @@ void Load()
|
||||||
for (auto& layer : s_layers)
|
for (auto& layer : s_layers)
|
||||||
layer.second->Load();
|
layer.second->Load();
|
||||||
}
|
}
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Save()
|
void Save()
|
||||||
|
@ -96,7 +105,7 @@ void Save()
|
||||||
for (auto& layer : s_layers)
|
for (auto& layer : s_layers)
|
||||||
layer.second->Save();
|
layer.second->Save();
|
||||||
}
|
}
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
|
@ -207,7 +216,7 @@ ConfigChangeCallbackGuard::~ConfigChangeCallbackGuard()
|
||||||
if (--s_callback_guards)
|
if (--s_callback_guards)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
|
@ -24,7 +24,10 @@ std::shared_ptr<Layer> GetLayer(LayerType layer);
|
||||||
void RemoveLayer(LayerType layer);
|
void RemoveLayer(LayerType layer);
|
||||||
|
|
||||||
void AddConfigChangedCallback(ConfigChangedCallback func);
|
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
|
// Explicit load and save of layers
|
||||||
void Load();
|
void Load();
|
||||||
|
@ -51,6 +54,23 @@ T Get(LayerType layer, const Info<T>& info)
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T Get(const Info<T>& info)
|
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());
|
const std::optional<std::string> str = GetAsString(info.GetLocation());
|
||||||
if (!str)
|
if (!str)
|
||||||
|
@ -75,7 +95,7 @@ template <typename T>
|
||||||
void Set(LayerType layer, const Info<T>& info, const std::common_type_t<T>& value)
|
void Set(LayerType layer, const Info<T>& info, const std::common_type_t<T>& value)
|
||||||
{
|
{
|
||||||
GetLayer(layer)->Set(info, value);
|
GetLayer(layer)->Set(info, value);
|
||||||
InvokeConfigChangedCallbacks();
|
OnConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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);
|
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
|
class ConfigChangeCallbackGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Config/Enums.h"
|
#include "Common/Config/Enums.h"
|
||||||
|
|
||||||
namespace Config
|
namespace Config
|
||||||
|
@ -29,30 +33,93 @@ struct Location
|
||||||
bool operator<(const Location& other) const;
|
bool operator<(const Location& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct CachedValue
|
||||||
|
{
|
||||||
|
T value;
|
||||||
|
u64 config_version;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Info
|
class Info
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr Info(const Location& location, const T& default_value)
|
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>>
|
// 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.
|
// so that enum settings can still easily work with code that doesn't care about the enum values.
|
||||||
template <typename Enum,
|
template <typename Enum,
|
||||||
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
|
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
|
||||||
constexpr Info(const Info<Enum>& other)
|
Info<T>& operator=(const Info<Enum>& other)
|
||||||
: m_location{other.GetLocation()}, m_default_value{static_cast<detail::UnderlyingType<Enum>>(
|
|
||||||
other.GetDefaultValue())}
|
|
||||||
{
|
{
|
||||||
|
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 Location& GetLocation() const { return m_location; }
|
||||||
constexpr const T& GetDefaultValue() const { return m_default_value; }
|
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:
|
private:
|
||||||
Location m_location;
|
Location m_location;
|
||||||
T m_default_value;
|
T m_default_value;
|
||||||
|
|
||||||
|
mutable CachedValue<T> m_cached_value;
|
||||||
|
mutable std::shared_mutex m_cached_value_mutex;
|
||||||
};
|
};
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
Loading…
Reference in New Issue