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

View File

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

View File

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

View File

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