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:
JosJuice 2020-12-05 18:24:41 +01:00
parent 633ab2dd7c
commit d8744e6db8
4 changed files with 110 additions and 14 deletions

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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