|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/observer_list.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/observer_list.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/time/time.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "testing/perf/perf_test.h" | 
|  |  | 
|  | // Ask the compiler not to use a register for this counter, in case it decides | 
|  | // to do magic optimizations like |counter += kLaps|. | 
|  | volatile int g_observer_list_perf_test_counter; | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | class ObserverInterface { | 
|  | public: | 
|  | ObserverInterface() {} | 
|  | virtual ~ObserverInterface() {} | 
|  | virtual void Observe() const { ++g_observer_list_perf_test_counter; } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(ObserverInterface); | 
|  | }; | 
|  |  | 
|  | class UnsafeObserver : public ObserverInterface {}; | 
|  |  | 
|  | class TestCheckedObserver : public CheckedObserver, public ObserverInterface {}; | 
|  |  | 
|  | template <class ObserverType> | 
|  | struct Pick { | 
|  | // The ObserverList type to use. Checked observers need to be in a checked | 
|  | // ObserverList. | 
|  | using ObserverListType = ObserverList<ObserverType>; | 
|  | static const char* GetName() { return "CheckedObserver"; } | 
|  | }; | 
|  | template <> | 
|  | struct Pick<UnsafeObserver> { | 
|  | using ObserverListType = ObserverList<ObserverInterface>::Unchecked; | 
|  | static const char* GetName() { return "UnsafeObserver"; } | 
|  | }; | 
|  |  | 
|  | template <class ObserverType> | 
|  | class ObserverListPerfTest : public ::testing::Test { | 
|  | public: | 
|  | using ObserverListType = typename Pick<ObserverType>::ObserverListType; | 
|  |  | 
|  | ObserverListPerfTest() {} | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(ObserverListPerfTest); | 
|  | }; | 
|  |  | 
|  | typedef ::testing::Types<UnsafeObserver, TestCheckedObserver> ObserverTypes; | 
|  | TYPED_TEST_CASE(ObserverListPerfTest, ObserverTypes); | 
|  |  | 
|  | // Performance test for base::ObserverList and Checked Observers. | 
|  | TYPED_TEST(ObserverListPerfTest, NotifyPerformance) { | 
|  | constexpr int kMaxObservers = 128; | 
|  | #if DCHECK_IS_ON() | 
|  | // The test takes about 100x longer in debug builds, mostly due to sequence | 
|  | // checker overheads when WeakPtr gets involved. | 
|  | constexpr int kLaps = 1000000; | 
|  | #else | 
|  | constexpr int kLaps = 100000000; | 
|  | #endif | 
|  | constexpr int kWarmupLaps = 100; | 
|  | std::vector<std::unique_ptr<TypeParam>> observers; | 
|  |  | 
|  | for (int observer_count = 0; observer_count <= kMaxObservers; | 
|  | observer_count = observer_count ? observer_count * 2 : 1) { | 
|  | typename TestFixture::ObserverListType list; | 
|  | for (int i = 0; i < observer_count; ++i) | 
|  | observers.push_back(std::make_unique<TypeParam>()); | 
|  | for (auto& o : observers) | 
|  | list.AddObserver(o.get()); | 
|  |  | 
|  | for (int i = 0; i < kWarmupLaps; ++i) { | 
|  | for (auto& o : list) | 
|  | o.Observe(); | 
|  | } | 
|  | g_observer_list_perf_test_counter = 0; | 
|  | const int weighted_laps = kLaps / (observer_count + 1); | 
|  |  | 
|  | TimeTicks start = TimeTicks::Now(); | 
|  | for (int i = 0; i < weighted_laps; ++i) { | 
|  | for (auto& o : list) | 
|  | o.Observe(); | 
|  | } | 
|  | TimeDelta duration = TimeTicks::Now() - start; | 
|  |  | 
|  | const char* name = Pick<TypeParam>::GetName(); | 
|  | observers.clear(); | 
|  |  | 
|  | EXPECT_EQ(observer_count * weighted_laps, | 
|  | g_observer_list_perf_test_counter); | 
|  | EXPECT_TRUE(observer_count == 0 || list.might_have_observers()); | 
|  |  | 
|  | std::string prefix = | 
|  | base::StringPrintf("ObserverListPerfTest_%d.", observer_count); | 
|  |  | 
|  | // A typical value is 3-20 nanoseconds per observe in Release, 1000-2000ns | 
|  | // in an optimized build with DCHECKs and 3000-6000ns in debug builds. | 
|  | perf_test::PrintResult( | 
|  | prefix, name, "NotifyPerformance", | 
|  | duration.InNanoseconds() / | 
|  | static_cast<double>(g_observer_list_perf_test_counter + | 
|  | weighted_laps), | 
|  | "ns/observe", true); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace base |