forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathobserver_list_perftest.cc
141 lines (116 loc) · 4.58 KB
/
observer_list_perftest.cc
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
131
132
133
134
135
136
137
138
139
140
141
// 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) {
const int weighted_laps = kLaps / (observer_count + 1);
TimeDelta duration;
{
TimeTicks start;
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;
start = TimeTicks::Now();
for (int i = 0; i < weighted_laps; ++i) {
for (auto& o : list) {
o.Observe();
}
}
duration = TimeTicks::Now() - start;
}
// The observers are no longer needed in this iteration, so reset the list
// to get ready for the next iteration. Be careful. We cannot invoke
// `observers.clear()` before destructing the `list`. Otherwise, we will
// see crashes caused by dangling pointers.
observers.clear();
EXPECT_EQ(observer_count * weighted_laps,
g_observer_list_perf_test_counter);
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