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
|