Improve folly::ThreadLocal perf
authorAndrii Grynenko <andrii@fb.com>
Thu, 14 Apr 2016 18:19:59 +0000 (11:19 -0700)
committerFacebook Github Bot 8 <facebook-github-bot-8-bot@fb.com>
Thu, 14 Apr 2016 18:20:28 +0000 (11:20 -0700)
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

folly/detail/ThreadLocalDetail.cpp
folly/detail/ThreadLocalDetail.h

index fcee5c7e4ee5eb5c7e60ea24d1026edf245a0b97..9e7fda6828716589bd2507caeff6359a0493778b 100644 (file)
@@ -26,12 +26,24 @@ StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)())
 }
 
 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));
+#endif
   DCHECK_GT(threadEntry->elementsCapacity, 0);
   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_);
-    meta.erase(threadEntry.get());
+    meta.erase(&(*threadEntry));
     // No need to hold the lock any longer; the ThreadEntry is private to this
     // thread now that it's been removed from meta.
   }
@@ -210,19 +222,6 @@ void StaticMetaBase::reserve(EntryID* id) {
   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
 PthreadKeyUnregister PthreadKeyUnregister::instance_;
 }}
index 1fa4844d3655bd1c4669738a75896dd84347f5a1..65ecfc672416528e7a0942e8b76a4c21637f74dc 100644 (file)
@@ -289,7 +289,7 @@ struct StaticMetaBase {
 // StaticMeta; you can specify multiple Tag types to break that lock.
 template <class Tag>
 struct StaticMeta : StaticMetaBase {
-  StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntry) {
+  StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntrySlow) {
 #if FOLLY_HAVE_PTHREAD_ATFORK
     int ret = pthread_atfork(
         /*prepare*/ &StaticMeta::preFork,
@@ -313,13 +313,31 @@ struct StaticMeta : StaticMetaBase {
     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() {
     auto& meta = instance();
     auto key = meta.pthreadKey_;
     ThreadEntry* threadEntry =
       static_cast<ThreadEntry*>(pthread_getspecific(key));
     if (!threadEntry) {
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+      static FOLLY_TLS ThreadEntry threadEntrySingleton;
+      threadEntry = &threadEntrySingleton;
+#else
       threadEntry = new ThreadEntry();
+#endif
       threadEntry->meta = &meta;
       int ret = pthread_setspecific(key, threadEntry);
       checkPosixError(ret, "pthread_setspecific failed");
@@ -327,15 +345,15 @@ struct StaticMeta : StaticMetaBase {
     return threadEntry;
   }
 
-  static ThreadEntry* getThreadEntry() {
+  inline static ThreadEntry* getThreadEntry() {
 #ifdef FOLLY_TLD_USE_FOLLY_TLS
     static FOLLY_TLS ThreadEntry* threadEntryCache{nullptr};
     if (UNLIKELY(threadEntryCache == nullptr)) {
-      threadEntryCache = getThreadEntrySlow();
+      threadEntryCache = instance().threadEntry_();
     }
     return threadEntryCache;
 #else
-    return getThreadEntrySlow();
+    return instance().threadEntry_();
 #endif
   }