| // 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 |