1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
base / observer_list_perftest.cc [blame]
// Copyright 2018 The Chromium Authors
// 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/check_op.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.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 {
constexpr char kMetricPrefixObserverList[] = "ObserverList.";
constexpr char kMetricNotifyTimePerObserver[] = "notify_time_per_observer";
namespace {
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
perf_test::PerfResultReporter reporter(kMetricPrefixObserverList, story_name);
reporter.RegisterImportantMetric(kMetricNotifyTimePerObserver, "ns");
return reporter;
}
} // namespace
class ObserverInterface {
public:
ObserverInterface() = default;
ObserverInterface(const ObserverInterface&) = delete;
ObserverInterface& operator=(const ObserverInterface&) = delete;
virtual ~ObserverInterface() = default;
virtual void Observe() const {
g_observer_list_perf_test_counter = g_observer_list_perf_test_counter + 1;
}
};
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() = default;
ObserverListPerfTest(const ObserverListPerfTest&) = delete;
ObserverListPerfTest& operator=(const ObserverListPerfTest&) = delete;
};
typedef ::testing::Types<UnsafeObserver, TestCheckedObserver> ObserverTypes;
TYPED_TEST_SUITE(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;
observers.clear();
EXPECT_EQ(observer_count * weighted_laps,
g_observer_list_perf_test_counter);
EXPECT_TRUE(observer_count == 0 || !list.empty());
std::string story_name =
base::StringPrintf("%s_%d", Pick<TypeParam>::GetName(), 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.
auto reporter = SetUpReporter(story_name);
reporter.AddResult(
kMetricNotifyTimePerObserver,
duration.InNanoseconds() /
static_cast<double>(g_observer_list_perf_test_counter +
weighted_laps));
}
}
} // namespace base