From 71c140ede54db347f9a971d6e2de0ade95569c1d Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Wed, 9 Dec 2015 15:09:05 -0800 Subject: [PATCH] Switch folly::Singleton to ReadMostlySharedPtr 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 | 19 ++- folly/Singleton.h | 14 +- folly/experimental/ReadMostlySharedPtr.h | 14 +- .../test/ReadMostlySharedPtrBenchmark.cpp | 1 + .../test/ReadMostlySharedPtrTest.cpp | 1 + folly/test/SingletonBenchmark.cpp | 159 ++++++++++++++++++ folly/test/SingletonTest.cpp | 128 +------------- 7 files changed, 202 insertions(+), 134 deletions(-) create mode 100644 folly/test/SingletonBenchmark.cpp diff --git a/folly/Singleton-inl.h b/folly/Singleton-inl.h index deca4b2c..e9a1963a 100644 --- a/folly/Singleton-inl.h +++ b/folly/Singleton-inl.h @@ -112,6 +112,16 @@ std::shared_ptr SingletonHolder::try_get() { return instance_weak_.lock(); } +template +folly::ReadMostlySharedPtr SingletonHolder::try_get_fast() { + if (UNLIKELY(state_.load(std::memory_order_acquire) != + SingletonHolderState::Living)) { + createInstance(); + } + + return instance_weak_fast_.lock(); +} + template TypeDescriptor SingletonHolder::type() { return type_; @@ -208,7 +218,7 @@ void SingletonHolder::createInstance() { auto type_name = type_.name(); // Can't use make_shared -- no support for a custom deleter, sadly. - instance_ = std::shared_ptr( + std::shared_ptr instance( create_(), [destroy_baton, print_destructor_stack_trace, teardown, type_name] (T* instance_ptr) mutable { @@ -236,8 +246,11 @@ void SingletonHolder::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); diff --git a/folly/Singleton.h b/folly/Singleton.h index af8dacdb..a4fdf203 100644 --- a/folly/Singleton.h +++ b/folly/Singleton.h @@ -110,6 +110,7 @@ #include #include #include +#include #include #include @@ -244,6 +245,7 @@ struct SingletonHolder : public SingletonHolderBase { inline T* get(); inline std::weak_ptr get_weak(); inline std::shared_ptr try_get(); + inline folly::ReadMostlySharedPtr 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 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 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 instance_weak_; + // Fast equivalent of instance_weak_ + folly::ReadMostlyWeakPtr instance_weak_fast_; // Time we wait on destroy_baton after releasing Singleton shared_ptr. std::shared_ptr> destroy_baton_; T* instance_ptr_ = nullptr; @@ -502,6 +506,10 @@ class Singleton { return getEntry().try_get(); } + static folly::ReadMostlySharedPtr 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)) { diff --git a/folly/experimental/ReadMostlySharedPtr.h b/folly/experimental/ReadMostlySharedPtr.h index d6c04cdb..e445e12c 100644 --- a/folly/experimental/ReadMostlySharedPtr.h +++ b/folly/experimental/ReadMostlySharedPtr.h @@ -18,7 +18,6 @@ #include -#include #include 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& ptr) { + reset(ptr.impl_); + } + ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) { *this = other; } @@ -206,6 +209,11 @@ class ReadMostlyWeakPtr { return *this; } + ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr& mainPtr) { + reset(mainPtr.impl_); + return *this; + } + ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept { *this = other; } @@ -335,6 +343,8 @@ class ReadMostlySharedPtr { } private: + friend class ReadMostlyWeakPtr; + void reset(detail::ReadMostlySharedPtrCore* impl) { if (impl_) { impl_->decref(); diff --git a/folly/experimental/test/ReadMostlySharedPtrBenchmark.cpp b/folly/experimental/test/ReadMostlySharedPtrBenchmark.cpp index 0d75c28a..d66b4f0a 100644 --- a/folly/experimental/test/ReadMostlySharedPtrBenchmark.cpp +++ b/folly/experimental/test/ReadMostlySharedPtrBenchmark.cpp @@ -21,6 +21,7 @@ #include #include +#include #include template class MainPtr, diff --git a/folly/experimental/test/ReadMostlySharedPtrTest.cpp b/folly/experimental/test/ReadMostlySharedPtrTest.cpp index 65b79309..ca07c25b 100644 --- a/folly/experimental/test/ReadMostlySharedPtrTest.cpp +++ b/folly/experimental/test/ReadMostlySharedPtrTest.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using folly::ReadMostlyMainPtr; diff --git a/folly/test/SingletonBenchmark.cpp b/folly/test/SingletonBenchmark.cpp new file mode 100644 index 00000000..f201cdbf --- /dev/null +++ b/folly/test/SingletonBenchmark.cpp @@ -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 +#include +#include +#include +#include + +#include + +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 f) { + std::vector 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 +using SingletonBenchmark = Singleton ; + +struct GetTag{}; +struct TryGetTag{}; +struct TryGetFastTag{}; + +SingletonBenchmark benchmark_singleton_get; +SingletonBenchmark benchmark_singleton_try_get; +SingletonBenchmark +benchmark_singleton_try_get_fast; + +void follySingletonRaw(size_t n) { + for (size_t i = 0; i < n; ++i) { + SingletonBenchmark::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::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::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; +} diff --git a/folly/test/SingletonTest.cpp b/folly/test/SingletonTest.cpp index 89423d45..c86200a3 100644 --- a/folly/test/SingletonTest.cpp +++ b/folly/test/SingletonTest.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -60,6 +59,7 @@ TEST(Singleton, BasicUsage) { EXPECT_NE(s2, nullptr); EXPECT_EQ(s1, s2); + EXPECT_EQ(s1.get(), SingletonBasicUsage::try_get_fast().get()); std::shared_ptr s3 = SingletonBasicUsage::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 using SingletonMock = Singleton ; @@ -595,114 +581,6 @@ TEST(Singleton, MockTest) { EXPECT_NE(serial_count_first, serial_count_mock); } -struct BenchmarkSingleton { - int val = 0; -}; - -void run4Threads(std::function f) { - std::vector 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 -using SingletonBenchmark = Singleton ; - -struct GetTag{}; -struct GetSharedTag{}; -struct GetWeakTag{}; - -SingletonBenchmark benchmark_singleton_get; -SingletonBenchmark -benchmark_singleton_get_shared; -SingletonBenchmark benchmark_singleton_get_weak; - -void follySingletonRaw(size_t n) { - for (size_t i = 0; i < n; ++i) { - SingletonBenchmark::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::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::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; } -- 2.34.1