chromium/mojo/core/handle_table_perftest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mojo/core/handle_table.h"

#include "base/memory/raw_ptr.h"
#include "base/synchronization/lock.h"
#include "base/timer/lap_timer.h"
#include "mojo/core/dispatcher.h"
#include "mojo/public/c/system/types.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"

namespace mojo::core {
namespace {

using ::base::LapTimer;
using ::perf_test::PerfResultReporter;
using ::testing::Eq;
using ::testing::Gt;
using ::testing::Ne;

class FakeMessagePipeDispatcherForTesting : public Dispatcher {
 public:
  FakeMessagePipeDispatcherForTesting() = default;

  FakeMessagePipeDispatcherForTesting(
      const FakeMessagePipeDispatcherForTesting&) = delete;
  FakeMessagePipeDispatcherForTesting& operator=(
      const FakeMessagePipeDispatcherForTesting&) = delete;

  Type GetType() const override { return Type::MESSAGE_PIPE; }
  MojoResult Close() override { return MOJO_RESULT_OK; }

 private:
  ~FakeMessagePipeDispatcherForTesting() override = default;
};

// Returns the handles of the dispatchers added.
std::vector<MojoHandle> AddDispatchersForTesting(
    const int num_dispatchers_to_add,
    HandleTable* handle_table) {
  std::vector<MojoHandle> handles;
  handles.reserve(num_dispatchers_to_add);
  scoped_refptr<Dispatcher> dispatcher(new FakeMessagePipeDispatcherForTesting);
  const base::AutoLock auto_lock(handle_table->GetLock());
  for (int i = 0; i < num_dispatchers_to_add; ++i) {
    const MojoHandle handle = handle_table->AddDispatcher(dispatcher);
    EXPECT_THAT(handle, Ne(MOJO_HANDLE_INVALID));
    handles.push_back(handle);
  }
  return handles;
}

constexpr char kMetricThroughput[] = "Throughput";

PerfResultReporter MakeReporter(const std::string& story_name) {
  PerfResultReporter reporter("HandleTable", story_name);
  reporter.RegisterImportantMetric(kMetricThroughput, "runs/s");
  return reporter;
}

}  // namespace

TEST(HandleTablePerfTest, GetDispatcherDifferentHandles) {
  // The number below is based on https://crbug.com/1295449#c2.
  constexpr int kNumDispatchers = 10000;
  HandleTable handle_table;
  const std::vector<MojoHandle> handles =
      AddDispatchersForTesting(kNumDispatchers, &handle_table);
  ASSERT_THAT(handles.size(), Gt(0ul));
  const int handles_last_index = handles.size() - 1;

  int current_index = 0;
  LapTimer timer;
  // Query for dispatchers in a round-robin manner until the time limit expires.
  while (!timer.HasTimeLimitExpired()) {
    handle_table.GetDispatcher(handles[current_index]);
    current_index = current_index == handles_last_index ? 0 : current_index + 1;
    timer.NextLap();
  }

  PerfResultReporter reporter = MakeReporter("GetDispatcherDifferentHandles");
  reporter.AddResult(kMetricThroughput, timer.LapsPerSecond());
}

TEST(HandleTablePerfTest, GetDispatcherSameHandle) {
  // The number below is based on https://crbug.com/1295449#c2.
  constexpr int kNumDispatchers = 10000;
  HandleTable handle_table;
  const std::vector<MojoHandle> handles =
      AddDispatchersForTesting(kNumDispatchers, &handle_table);
  ASSERT_THAT(handles.size(), Gt(0ul));

  LapTimer timer;
  while (!timer.HasTimeLimitExpired()) {
    handle_table.GetDispatcher(handles[0]);
    timer.NextLap();
  }

  PerfResultReporter reporter = MakeReporter("GetDispatcherSameHandle");
  reporter.AddResult(kMetricThroughput, timer.LapsPerSecond());
}

TEST(HandleTablePerfTest, GetDispatcherMixedHandles) {
  // The number below is based on https://crbug.com/1295449#c2.
  constexpr int kNumDispatchers = 10000;
  HandleTable handle_table;
  const std::vector<MojoHandle> handles =
      AddDispatchersForTesting(kNumDispatchers, &handle_table);
  ASSERT_THAT(handles.size(), Gt(0ul));
  const int handles_last_index = handles.size() - 1;

  int current_index = 0;
  LapTimer timer;
  while (!timer.HasTimeLimitExpired()) {
    // Sample each index 3 times, thus sampling the same index as the previous
    // one roughly 66% of the time. Based on https://crbug.com/1295449.
    handle_table.GetDispatcher(handles[current_index / 4]);
    current_index = current_index == handles_last_index ? 0 : current_index + 1;
    timer.NextLap();
  }

  PerfResultReporter reporter = MakeReporter("GetDispatcherMixedHandles");
  reporter.AddResult(kMetricThroughput, timer.LapsPerSecond());
}

TEST(HandleTablePerfTest, AddAndRemoveDispatcher) {
  // The number below is based on https://crbug.com/1295449#c2.
  constexpr int kNumDispatchers = 10000;
  HandleTable handle_table;
  const std::vector<MojoHandle> handles =
      AddDispatchersForTesting(kNumDispatchers, &handle_table);
  ASSERT_THAT(handles.size(), Gt(0ul));

  LapTimer timer;
  while (!timer.HasTimeLimitExpired()) {
    const base::AutoLock auto_lock(handle_table.GetLock());
    scoped_refptr<Dispatcher> dispatcher(
        new FakeMessagePipeDispatcherForTesting);
    const MojoHandle handle = handle_table.AddDispatcher(std::move(dispatcher));
    EXPECT_THAT(handle, Ne(MOJO_HANDLE_INVALID));
    const MojoResult result =
        handle_table.GetAndRemoveDispatcher(handle, &dispatcher);
    EXPECT_THAT(result, Eq(MOJO_RESULT_OK));
    timer.NextLap();
  }

  PerfResultReporter reporter = MakeReporter("AddAndRemoveDispatcher");
  reporter.AddResult(kMetricThroughput, timer.LapsPerSecond());
}

}  // namespace mojo::core