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