Commit ecdcc6d8 authored by Anton Likhtarov's avatar Anton Likhtarov Committed by Facebook Github Bot

Perf: specialization for small types

Summary:
- Change the get() API to return T by value if T is small
- Internally store small T in a global atomic to avoid thread local lookups

Differential Revision: D9982433

fbshipit-source-id: c42d06a75b9ff48307a90f77d0bb72bfa6375d6c
parent 3a2d6a36
......@@ -66,17 +66,19 @@ template <class T>
class Setting {
public:
/**
* Returns the setting's current value. Note that the returned
* reference is not guaranteed to be long-lived and should not be
* saved anywhere. In particular, a set() call might invalidate a
* reference obtained here after some amount of time (on the order
* of minutes).
* Returns the setting's current value.
*
* As an optimization, returns by value for small types, and by
* const& for larger types. Note that the returned reference is not
* guaranteed to be long-lived and should not be saved anywhere. In
* particular, a set() call might invalidate a reference obtained
* here after some amount of time (on the order of minutes).
*/
const T& operator*() const {
std::conditional_t<IsSmallPOD<T>::value, T, const T&> operator*() const {
return core_.get();
}
const T* operator->() const {
return &core_.get();
return &core_.getSlow();
}
/**
......@@ -93,8 +95,12 @@ class Setting {
core_.set(t, reason);
}
Setting(SettingMetadata meta, T defaultValue)
: meta_(std::move(meta)), core_(meta_, std::move(defaultValue)) {}
Setting(
SettingMetadata meta,
T defaultValue,
std::atomic<uint64_t>& trivialStorage)
: meta_(std::move(meta)),
core_(meta_, std::move(defaultValue), trivialStorage) {}
private:
SettingMetadata meta_;
......@@ -170,6 +176,11 @@ using TypeIdentityT = typename TypeIdentity<T>::type;
__attribute__((__section__(".folly.settings.cache"))) \
std::atomic<folly::settings::detail::Setting<_Type>*> \
FOLLY_SETTINGS_CACHE__##_project##_##_name; \
/* Location for the small value cache (if _Type is small and trivial). \
Intentionally located right after the pointer cache above to take \
advantage of the prefetching */ \
__attribute__((__section__(".folly.settings.cache"))) std::atomic<uint64_t> \
FOLLY_SETTINGS_TRIVIAL__##_project##_##_name; \
/* Meyers singleton to avoid SIOF */ \
FOLLY_NOINLINE folly::settings::detail::Setting<_Type>& \
FOLLY_SETTINGS_FUNC__##_project##_##_name() { \
......@@ -177,7 +188,8 @@ using TypeIdentityT = typename TypeIdentity<T>::type;
setting( \
folly::settings::SettingMetadata{ \
#_project, #_name, #_Type, typeid(_Type), #_def, _desc}, \
folly::settings::detail::TypeIdentityT<_Type>{_def}); \
folly::settings::detail::TypeIdentityT<_Type>{_def}, \
FOLLY_SETTINGS_TRIVIAL__##_project##_##_name); \
return *setting; \
} \
/* Ensure the setting is registered even if not used in program */ \
......
......@@ -33,6 +33,15 @@ struct SettingMetadata;
namespace detail {
/**
* Can we store T in a global atomic?
*/
template <class T>
struct IsSmallPOD
: std::integral_constant<
bool,
std::is_trivial<T>::value && sizeof(T) <= sizeof(uint64_t)> {};
template <class T>
struct SettingContents {
std::string updateReason;
......@@ -85,24 +94,54 @@ class SettingCore : public SettingCoreBase {
return meta_;
}
const T& get() const {
std::conditional_t<IsSmallPOD<T>::value, T, const T&> get() const {
return getImpl(IsSmallPOD<T>(), trivialStorage_);
}
const T& getSlow() const {
return getImpl(std::false_type{}, trivialStorage_);
}
/***
* SmallPOD version: just read the global atomic
*/
T getImpl(std::true_type, std::atomic<uint64_t>& trivialStorage) const {
uint64_t v = trivialStorage.load();
T t;
std::memcpy(&t, &v, sizeof(T));
return t;
}
/**
* Non-SmallPOD version: read the thread local shared_ptr
*/
const T& getImpl(std::false_type, std::atomic<uint64_t>& /* ignored */)
const {
return const_cast<SettingCore*>(this)->tlValue()->value;
}
void set(const T& t, StringPiece reason) {
SharedMutex::WriteHolder lg(globalLock_);
globalValue_ = std::make_shared<Contents>(reason.str(), t);
if (IsSmallPOD<T>::value) {
uint64_t v = 0;
std::memcpy(&v, &t, sizeof(T));
trivialStorage_.store(v);
}
++(*globalVersion_);
}
SettingCore(const SettingMetadata& meta, T defaultValue)
SettingCore(
const SettingMetadata& meta,
T defaultValue,
std::atomic<uint64_t>& trivialStorage)
: meta_(meta),
defaultValue_(std::move(defaultValue)),
globalValue_(std::make_shared<Contents>("default", defaultValue_)),
trivialStorage_(trivialStorage),
localValue_([]() {
return new CachelinePadded<
Indestructible<std::pair<size_t, std::shared_ptr<Contents>>>>(
0, nullptr);
}) {
set(defaultValue_, "default");
registerSetting(*this);
}
......@@ -113,6 +152,8 @@ class SettingCore : public SettingCoreBase {
SharedMutex globalLock_;
std::shared_ptr<Contents> globalValue_;
std::atomic<uint64_t>& trivialStorage_;
/* Local versions start at 0, this will force a read on first local access. */
CachelinePadded<std::atomic<size_t>> globalVersion_{1};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment