Switch folly::Singleton to ReadMostlySharedPtr
authorAndrii Grynenko <andrii@fb.com>
Wed, 9 Dec 2015 23:09:05 +0000 (15:09 -0800)
committerfacebook-github-bot-4 <folly-bot@fb.com>
Wed, 9 Dec 2015 23:20:29 +0000 (15:20 -0800)
Summary:
This also introduces a new try_get_fast() API which returns ReadMostlySharedPtr.
We should eventually migrate all users onto this API.

Reviewed By: elsteveogrande

Differential Revision: D2706745

fb-gh-sync-id: d558d705f431fc6147334c188fd0c6156644ba5c

folly/Singleton-inl.h
folly/Singleton.h
folly/experimental/ReadMostlySharedPtr.h
folly/experimental/test/ReadMostlySharedPtrBenchmark.cpp
folly/experimental/test/ReadMostlySharedPtrTest.cpp
folly/test/SingletonBenchmark.cpp [new file with mode: 0644]
folly/test/SingletonTest.cpp

index deca4b2c9ddcc64346767ac139d67e689790f04d..e9a1963a480233f41b1422c6d0376b5924e73583 100644 (file)
@@ -112,6 +112,16 @@ std::shared_ptr<T> SingletonHolder<T>::try_get() {
   return instance_weak_.lock();
 }
 
+template <typename T>
+folly::ReadMostlySharedPtr<T> SingletonHolder<T>::try_get_fast() {
+  if (UNLIKELY(state_.load(std::memory_order_acquire) !=
+               SingletonHolderState::Living)) {
+    createInstance();
+  }
+
+  return instance_weak_fast_.lock();
+}
+
 template <typename T>
 TypeDescriptor SingletonHolder<T>::type() {
   return type_;
@@ -208,7 +218,7 @@ void SingletonHolder<T>::createInstance() {
   auto type_name = type_.name();
 
   // Can't use make_shared -- no support for a custom deleter, sadly.
-  instance_ = std::shared_ptr<T>(
+  std::shared_ptr<T> instance(
     create_(),
     [destroy_baton, print_destructor_stack_trace, teardown, type_name]
     (T* instance_ptr) mutable {
@@ -236,8 +246,11 @@ void SingletonHolder<T>::createInstance() {
   // constructor
   SingletonVault::scheduleDestroyInstances();
 
-  instance_weak_ = instance_;
-  instance_ptr_ = instance_.get();
+  instance_weak_ = instance;
+  instance_ptr_ = instance.get();
+  instance_.reset(std::move(instance));
+  instance_weak_fast_ = instance_;
+
   destroy_baton_ = std::move(destroy_baton);
   print_destructor_stack_trace_ = std::move(print_destructor_stack_trace);
 
index af8dacdb6d256acc6a4796e5d526da3354189c9d..a4fdf2036a00ef72c7d9ca92c355fe9406b0a65d 100644 (file)
 #include <folly/Demangle.h>
 #include <folly/Executor.h>
 #include <folly/io/async/Request.h>
+#include <folly/experimental/ReadMostlySharedPtr.h>
 
 #include <algorithm>
 #include <atomic>
@@ -244,6 +245,7 @@ struct SingletonHolder : public SingletonHolderBase {
   inline T* get();
   inline std::weak_ptr<T> get_weak();
   inline std::shared_ptr<T> try_get();
+  inline folly::ReadMostlySharedPtr<T> try_get_fast();
 
   void registerSingleton(CreateFunc c, TeardownFunc t);
   void registerSingletonMock(CreateFunc c, TeardownFunc t);
@@ -277,14 +279,16 @@ struct SingletonHolder : public SingletonHolderBase {
 
   // The singleton itself and related functions.
 
-  // holds a shared_ptr to singleton instance, set when state is changed from
-  // Dead to Living. Reset when state is changed from Living to Dead.
-  std::shared_ptr<T> instance_;
+  // holds a ReadMostlyMainPtr to singleton instance, set when state is changed
+  // from Dead to Living. Reset when state is changed from Living to Dead.
+  folly::ReadMostlyMainPtr<T> instance_;
   // weak_ptr to the singleton instance, set when state is changed from Dead
   // to Living. We never write to this object after initialization, so it is
   // safe to read it from different threads w/o synchronization if we know
   // that state is set to Living
   std::weak_ptr<T> instance_weak_;
+  // Fast equivalent of instance_weak_
+  folly::ReadMostlyWeakPtr<T> instance_weak_fast_;
   // Time we wait on destroy_baton after releasing Singleton shared_ptr.
   std::shared_ptr<folly::Baton<>> destroy_baton_;
   T* instance_ptr_ = nullptr;
@@ -502,6 +506,10 @@ class Singleton {
     return getEntry().try_get();
   }
 
+  static folly::ReadMostlySharedPtr<T> try_get_fast() {
+    return getEntry().try_get_fast();
+  }
+
   explicit Singleton(std::nullptr_t _ = nullptr,
                      typename Singleton::TeardownFunc t = nullptr) :
       Singleton ([]() { return new T; }, std::move(t)) {
index d6c04cdbcea4b9d5855f229bb400bf334bda65bb..e445e12cdccd7739f6bca54a821e31c73863415e 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <atomic>
 
-#include <folly/experimental/RCURefCount.h>
 #include <folly/experimental/TLRefCount.h>
 
 namespace folly {
@@ -60,7 +59,7 @@ class ReadMostlySharedPtrCore {
 
   void increfWeak() {
     auto value = ++weakCount_;
-    assert(value > 0);
+    DCHECK_GT(value, 0);
   }
 
   void decrefWeak() {
@@ -197,6 +196,10 @@ class ReadMostlyWeakPtr {
     reset(mainPtr.impl_);
   }
 
+  explicit ReadMostlyWeakPtr(const ReadMostlySharedPtr<T, RefCount>& ptr) {
+    reset(ptr.impl_);
+  }
+
   ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) {
     *this = other;
   }
@@ -206,6 +209,11 @@ class ReadMostlyWeakPtr {
     return *this;
   }
 
+  ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& mainPtr) {
+    reset(mainPtr.impl_);
+    return *this;
+  }
+
   ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept {
     *this = other;
   }
@@ -335,6 +343,8 @@ class ReadMostlySharedPtr {
   }
 
  private:
+  friend class ReadMostlyWeakPtr<T, RefCount>;
+
   void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) {
     if (impl_) {
       impl_->decref();
index 0d75c28a4f7ac0eca4e9b4257bc2736b41950e71..d66b4f0aaf626ffea13d224cc829e07510243bf4 100644 (file)
@@ -21,6 +21,7 @@
 #include <folly/Memory.h>
 #include <gflags/gflags.h>
 
+#include <folly/experimental/RCURefCount.h>
 #include <folly/experimental/ReadMostlySharedPtr.h>
 
 template <template<typename> class MainPtr,
index 65b793095bf876feac96ea50db556564fda3e415..ca07c25b8631bd496e9e4a4b7ea59df9f9686b77 100644 (file)
@@ -23,6 +23,7 @@
 #include <gtest/gtest.h>
 
 #include <folly/Baton.h>
+#include <folly/experimental/RCURefCount.h>
 #include <folly/experimental/ReadMostlySharedPtr.h>
 
 using folly::ReadMostlyMainPtr;
diff --git a/folly/test/SingletonBenchmark.cpp b/folly/test/SingletonBenchmark.cpp
new file mode 100644 (file)
index 0000000..f201cdb
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* -*- Mode: C++; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+#include <thread>
+#include <iostream>
+#include <folly/Benchmark.h>
+#include <folly/Memory.h>
+#include <gflags/gflags.h>
+
+#include <folly/Singleton.h>
+
+using namespace folly;
+
+// Benchmarking a normal singleton vs a Meyers singleton vs a Folly
+// singleton.  Meyers are insanely fast, but (hopefully) Folly
+// singletons are fast "enough."
+int* getMeyersSingleton() {
+  static auto ret = new int(0);
+  return ret;
+}
+
+int normal_singleton_value = 0;
+int* getNormalSingleton() {
+  doNotOptimizeAway(&normal_singleton_value);
+  return &normal_singleton_value;
+}
+
+struct BenchmarkSingleton {
+  int val = 0;
+};
+
+void run4Threads(std::function<void()> f) {
+  std::vector<std::thread> threads;
+  for (size_t i = 0; i < 4; ++ i) {
+    threads.emplace_back(f);
+  }
+  for (auto& thread : threads) {
+    thread.join();
+  }
+}
+
+void normalSingleton(size_t n) {
+  for (size_t i = 0; i < n; ++ i) {
+    doNotOptimizeAway(getNormalSingleton());
+  }
+}
+
+BENCHMARK(NormalSingleton, n) {
+  normalSingleton(n);
+}
+
+BENCHMARK(NormalSingleton4Threads, n) {
+  run4Threads([=]() {
+      normalSingleton(n);
+    });
+}
+
+void meyersSingleton(size_t n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(getMeyersSingleton());
+  }
+}
+
+
+BENCHMARK(MeyersSingleton, n) {
+  meyersSingleton(n);
+}
+
+BENCHMARK(MeyersSingleton4Threads, n) {
+  run4Threads([=]() {
+      meyersSingleton(n);
+    });
+}
+
+struct BenchmarkTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
+
+struct GetTag{};
+struct TryGetTag{};
+struct TryGetFastTag{};
+
+SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
+SingletonBenchmark<BenchmarkSingleton, TryGetTag> benchmark_singleton_try_get;
+SingletonBenchmark<BenchmarkSingleton, TryGetFastTag>
+benchmark_singleton_try_get_fast;
+
+void follySingletonRaw(size_t n) {
+  for (size_t i = 0; i < n; ++i) {
+    SingletonBenchmark<BenchmarkSingleton, GetTag>::get();
+  }
+}
+
+BENCHMARK(FollySingletonRaw, n) {
+  follySingletonRaw(n);
+}
+
+BENCHMARK(FollySingletonRaw4Threads, n) {
+  run4Threads([=]() {
+      follySingletonRaw(n);
+    });
+}
+
+void follySingletonTryGet(size_t n) {
+  for (size_t i = 0; i < n; ++i) {
+    SingletonBenchmark<BenchmarkSingleton, TryGetTag>::try_get();
+  }
+}
+
+BENCHMARK(FollySingletonTryGet, n) {
+  follySingletonTryGet(n);
+}
+
+BENCHMARK(FollySingletonTryGet4Threads, n) {
+  run4Threads([=]() {
+      follySingletonTryGet(n);
+    });
+}
+
+void follySingletonTryGetFast(size_t n) {
+  for (size_t i = 0; i < n; ++i) {
+    SingletonBenchmark<BenchmarkSingleton, TryGetFastTag>::try_get_fast();
+  }
+}
+
+BENCHMARK(FollySingletonTryGetFast, n) {
+  follySingletonTryGetFast(n);
+}
+
+BENCHMARK(FollySingletonTryGetFast4Threads, n) {
+  run4Threads([=]() {
+      follySingletonTryGetFast(n);
+    });
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  gflags::SetCommandLineOptionWithMode(
+    "bm_min_usec", "100000", gflags::SET_FLAG_IF_DEFAULT
+  );
+
+  folly::runBenchmarks();
+
+  return 0;
+}
index 89423d45cd109bc56a131436da893d1445ae924f..c86200a3d8472c36b37eed389578ab9aff9fbfb3 100644 (file)
@@ -19,7 +19,6 @@
 #include <folly/Singleton.h>
 #include <folly/io/async/EventBase.h>
 #include <folly/test/SingletonTestStructs.h>
-#include <folly/Benchmark.h>
 
 #include <glog/logging.h>
 #include <gtest/gtest.h>
@@ -60,6 +59,7 @@ TEST(Singleton, BasicUsage) {
     EXPECT_NE(s2, nullptr);
 
     EXPECT_EQ(s1, s2);
+    EXPECT_EQ(s1.get(), SingletonBasicUsage<Watchdog>::try_get_fast().get());
 
     std::shared_ptr<ChildWatchdog> s3 =
       SingletonBasicUsage<ChildWatchdog>::try_get();
@@ -554,20 +554,6 @@ TEST(Singleton, SingletonEagerInitParallel) {
   }
 }
 
-// Benchmarking a normal singleton vs a Meyers singleton vs a Folly
-// singleton.  Meyers are insanely fast, but (hopefully) Folly
-// singletons are fast "enough."
-int* getMeyersSingleton() {
-  static auto ret = new int(0);
-  return ret;
-}
-
-int normal_singleton_value = 0;
-int* getNormalSingleton() {
-  doNotOptimizeAway(&normal_singleton_value);
-  return &normal_singleton_value;
-}
-
 struct MockTag {};
 template <typename T, typename Tag = detail::DefaultTag>
 using SingletonMock = Singleton <T, Tag, MockTag>;
@@ -595,114 +581,6 @@ TEST(Singleton, MockTest) {
   EXPECT_NE(serial_count_first, serial_count_mock);
 }
 
-struct BenchmarkSingleton {
-  int val = 0;
-};
-
-void run4Threads(std::function<void()> f) {
-  std::vector<std::thread> threads;
-  for (size_t i = 0; i < 4; ++ i) {
-    threads.emplace_back(f);
-  }
-  for (auto& thread : threads) {
-    thread.join();
-  }
-}
-
-void normalSingleton(size_t n) {
-  for (size_t i = 0; i < n; ++ i) {
-    doNotOptimizeAway(getNormalSingleton());
-  }
-}
-
-BENCHMARK(NormalSingleton, n) {
-  normalSingleton(n);
-}
-
-BENCHMARK(NormalSingleton4Threads, n) {
-  run4Threads([=]() {
-      normalSingleton(n);
-    });
-}
-
-void meyersSingleton(size_t n) {
-  for (size_t i = 0; i < n; ++i) {
-    doNotOptimizeAway(getMeyersSingleton());
-  }
-}
-
-
-BENCHMARK_RELATIVE(MeyersSingleton, n) {
-  meyersSingleton(n);
-}
-
-BENCHMARK_RELATIVE(MeyersSingleton4Threads, n) {
-  run4Threads([=]() {
-      meyersSingleton(n);
-    });
-}
-
-struct BenchmarkTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
-
-struct GetTag{};
-struct GetSharedTag{};
-struct GetWeakTag{};
-
-SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
-SingletonBenchmark<BenchmarkSingleton, GetSharedTag>
-benchmark_singleton_get_shared;
-SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
-
-void follySingletonRaw(size_t n) {
-  for (size_t i = 0; i < n; ++i) {
-    SingletonBenchmark<BenchmarkSingleton, GetTag>::get();
-  }
-}
-
-BENCHMARK_RELATIVE(FollySingletonRaw, n) {
-  follySingletonRaw(n);
-}
-
-BENCHMARK_RELATIVE(FollySingletonRaw4Threads, n) {
-  run4Threads([=]() {
-      follySingletonRaw(n);
-    });
-}
-
-void follySingletonSharedPtr(size_t n) {
-  for (size_t i = 0; i < n; ++i) {
-    SingletonBenchmark<BenchmarkSingleton, GetSharedTag>::try_get();
-  }
-}
-
-BENCHMARK_RELATIVE(FollySingletonSharedPtr, n) {
-  follySingletonSharedPtr(n);
-}
-
-BENCHMARK_RELATIVE(FollySingletonSharedPtr4Threads, n) {
-  run4Threads([=]() {
-      follySingletonSharedPtr(n);
-    });
-}
-
-void follySingletonWeakPtr(size_t n) {
-  for (size_t i = 0; i < n; ++i) {
-    SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
-  }
-}
-
-BENCHMARK_RELATIVE(FollySingletonWeakPtr, n) {
-  follySingletonWeakPtr(n);
-}
-
-BENCHMARK_RELATIVE(FollySingletonWeakPtr4Threads, n) {
-  run4Threads([=]() {
-      follySingletonWeakPtr(n);
-    });
-}
-
 int main(int argc, char* argv[]) {
   testing::InitGoogleTest(&argc, argv);
   google::InitGoogleLogging(argv[0]);
@@ -711,8 +589,6 @@ int main(int argc, char* argv[]) {
   SingletonVault::singleton()->registrationComplete();
 
   auto ret = RUN_ALL_TESTS();
-  if (!ret) {
-    folly::runBenchmarksOnFlag();
-  }
+
   return ret;
 }