Commit b25183f6 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot 8

Improve folly::ThreadLocal perf

Summary: This fixes folly::ThreadLocal perf after several refactorings which touched the code.

Reviewed By: ericniebler

Differential Revision: D3170761

fb-gh-sync-id: d44198f19aebc9f9f2588a65950efba0694e2a11
fbshipit-source-id: d44198f19aebc9f9f2588a65950efba0694e2a11
parent a6692f12
...@@ -26,12 +26,24 @@ StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)()) ...@@ -26,12 +26,24 @@ StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)())
} }
void StaticMetaBase::onThreadExit(void* ptr) { void StaticMetaBase::onThreadExit(void* ptr) {
#ifdef FOLLY_TLD_USE_FOLLY_TLS
auto threadEntry = static_cast<ThreadEntry*>(ptr);
#else
std::unique_ptr<ThreadEntry> threadEntry(static_cast<ThreadEntry*>(ptr)); std::unique_ptr<ThreadEntry> threadEntry(static_cast<ThreadEntry*>(ptr));
#endif
DCHECK_GT(threadEntry->elementsCapacity, 0); DCHECK_GT(threadEntry->elementsCapacity, 0);
auto& meta = *threadEntry->meta; auto& meta = *threadEntry->meta;
// Make sure this ThreadEntry is available if ThreadLocal A is accessed in
// ThreadLocal B destructor.
pthread_setspecific(meta.pthreadKey_, threadEntry);
SCOPE_EXIT {
pthread_setspecific(meta.pthreadKey_, nullptr);
};
{ {
std::lock_guard<std::mutex> g(meta.lock_); std::lock_guard<std::mutex> g(meta.lock_);
meta.erase(threadEntry.get()); meta.erase(&(*threadEntry));
// No need to hold the lock any longer; the ThreadEntry is private to this // No need to hold the lock any longer; the ThreadEntry is private to this
// thread now that it's been removed from meta. // thread now that it's been removed from meta.
} }
...@@ -210,19 +222,6 @@ void StaticMetaBase::reserve(EntryID* id) { ...@@ -210,19 +222,6 @@ void StaticMetaBase::reserve(EntryID* id) {
free(reallocated); free(reallocated);
} }
ElementWrapper& StaticMetaBase::get(EntryID* ent) {
ThreadEntry* threadEntry = (*threadEntry_)();
uint32_t id = ent->getOrInvalid();
// if id is invalid, it is equal to uint32_t's max value.
// x <= max value is always true
if (UNLIKELY(threadEntry->elementsCapacity <= id)) {
reserve(ent);
id = ent->getOrInvalid();
assert(threadEntry->elementsCapacity > id);
}
return threadEntry->elements[id];
}
FOLLY_STATIC_CTOR_PRIORITY_MAX FOLLY_STATIC_CTOR_PRIORITY_MAX
PthreadKeyUnregister PthreadKeyUnregister::instance_; PthreadKeyUnregister PthreadKeyUnregister::instance_;
}} }}
...@@ -289,7 +289,7 @@ struct StaticMetaBase { ...@@ -289,7 +289,7 @@ struct StaticMetaBase {
// StaticMeta; you can specify multiple Tag types to break that lock. // StaticMeta; you can specify multiple Tag types to break that lock.
template <class Tag> template <class Tag>
struct StaticMeta : StaticMetaBase { struct StaticMeta : StaticMetaBase {
StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntry) { StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntrySlow) {
#if FOLLY_HAVE_PTHREAD_ATFORK #if FOLLY_HAVE_PTHREAD_ATFORK
int ret = pthread_atfork( int ret = pthread_atfork(
/*prepare*/ &StaticMeta::preFork, /*prepare*/ &StaticMeta::preFork,
...@@ -313,13 +313,31 @@ struct StaticMeta : StaticMetaBase { ...@@ -313,13 +313,31 @@ struct StaticMeta : StaticMetaBase {
return *instance; return *instance;
} }
ElementWrapper& get(EntryID* ent) {
ThreadEntry* threadEntry = getThreadEntry();
uint32_t id = ent->getOrInvalid();
// if id is invalid, it is equal to uint32_t's max value.
// x <= max value is always true
if (UNLIKELY(threadEntry->elementsCapacity <= id)) {
reserve(ent);
id = ent->getOrInvalid();
assert(threadEntry->elementsCapacity > id);
}
return threadEntry->elements[id];
}
static ThreadEntry* getThreadEntrySlow() { static ThreadEntry* getThreadEntrySlow() {
auto& meta = instance(); auto& meta = instance();
auto key = meta.pthreadKey_; auto key = meta.pthreadKey_;
ThreadEntry* threadEntry = ThreadEntry* threadEntry =
static_cast<ThreadEntry*>(pthread_getspecific(key)); static_cast<ThreadEntry*>(pthread_getspecific(key));
if (!threadEntry) { if (!threadEntry) {
#ifdef FOLLY_TLD_USE_FOLLY_TLS
static FOLLY_TLS ThreadEntry threadEntrySingleton;
threadEntry = &threadEntrySingleton;
#else
threadEntry = new ThreadEntry(); threadEntry = new ThreadEntry();
#endif
threadEntry->meta = &meta; threadEntry->meta = &meta;
int ret = pthread_setspecific(key, threadEntry); int ret = pthread_setspecific(key, threadEntry);
checkPosixError(ret, "pthread_setspecific failed"); checkPosixError(ret, "pthread_setspecific failed");
...@@ -327,15 +345,15 @@ struct StaticMeta : StaticMetaBase { ...@@ -327,15 +345,15 @@ struct StaticMeta : StaticMetaBase {
return threadEntry; return threadEntry;
} }
static ThreadEntry* getThreadEntry() { inline static ThreadEntry* getThreadEntry() {
#ifdef FOLLY_TLD_USE_FOLLY_TLS #ifdef FOLLY_TLD_USE_FOLLY_TLS
static FOLLY_TLS ThreadEntry* threadEntryCache{nullptr}; static FOLLY_TLS ThreadEntry* threadEntryCache{nullptr};
if (UNLIKELY(threadEntryCache == nullptr)) { if (UNLIKELY(threadEntryCache == nullptr)) {
threadEntryCache = getThreadEntrySlow(); threadEntryCache = instance().threadEntry_();
} }
return threadEntryCache; return threadEntryCache;
#else #else
return getThreadEntrySlow(); return instance().threadEntry_();
#endif #endif
} }
......
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