Add CPU usage perf tests for legacy IPC and Mojo.
These tests do async/sync ping-pongs between two processes for legacy IPC/Mojo.
They do it in a somewhat fixed-rate manner: split one second into a fixed
amount of "frames", and do a fixed amount of ping-pongs for each frame.
Current numbers:
Linux; release component build; Z620
Test name description:
* Async / Sync: Whether ping-pong is done using async messages or sync messages.
* MsgSize: Size of each message, in bytes.
* FrmPerSec: Frames per second.
* MsgPerFrm: How many messages sent and received (i.e., round trips) per frame.
Test result: The percentage of a CPU core used during the test, on average.
(It could be the sum of cost on multiple cores.)
[ RUN ] ChannelSteadyPingPongTest.AsyncPingPong
IPC_CPU_Async_MsgSize_144_FrmPerSec_20_MsgPerFrm_10 5.68031 %
IPC_CPU_Async_MsgSize_144_FrmPerSec_60_MsgPerFrm_10 15.7017 %
[ RUN ] ChannelSteadyPingPongTest.SyncPingPong
IPC_CPU_Sync_MsgSize_144_FrmPerSec_20_MsgPerFrm_10 5.68387 %
IPC_CPU_Sync_MsgSize_144_FrmPerSec_60_MsgPerFrm_10 15.4848 %
[ RUN ] MojoSteadyPingPongTest.AsyncPingPong
Mojo_CPU_Async_MsgSize_144_FrmPerSec_20_MsgPerFrm_10 3.68818 %
Mojo_CPU_Async_MsgSize_144_FrmPerSec_60_MsgPerFrm_10 10.2393 %
[ RUN ] MojoSteadyPingPongTest.SyncPingPong
Mojo_CPU_Sync_MsgSize_144_FrmPerSec_20_MsgPerFrm_10 3.69054 %
Mojo_CPU_Sync_MsgSize_144_FrmPerSec_60_MsgPerFrm_10 10.7317 %
Bug: 759845
Change-Id: Idc8139a90c3175193aab68a8fc91d1aeac1aa340
Reviewed-on: https://chromium-review.googlesource.com/639340
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: John Abd-El-Malek <[email protected]>
Commit-Queue: Yuzhu Shen <[email protected]>
Cr-Commit-Position: refs/heads/master@{#500074}
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 42621399..5753bd5 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -250,7 +250,12 @@
test("ipc_perftests") {
sources = [
+ "ipc_cpu_perftest.cc",
"ipc_mojo_perftest.cc",
+ "ipc_perftest_messages.cc",
+ "ipc_perftest_messages.h",
+ "ipc_perftest_util.cc",
+ "ipc_perftest_util.h",
"run_all_perftests.cc",
]
diff --git a/ipc/DEPS b/ipc/DEPS
index f8d30bb..662e341 100644
--- a/ipc/DEPS
+++ b/ipc/DEPS
@@ -14,7 +14,8 @@
"+mojo/edk/embedder",
"+mojo/edk/test",
],
- "ipc_perftest_support\.cc": [
+ "ipc_.*perftest.*\.cc": [
+ "+mojo/edk/embedder",
"+mojo/edk/test",
],
"run_all_(unit|perf)tests\.cc": [
diff --git a/ipc/OWNERS b/ipc/OWNERS
index 0617e7bf..22545c9 100644
--- a/ipc/OWNERS
+++ b/ipc/OWNERS
@@ -8,6 +8,8 @@
# new sandbox escapes.
per-file ipc_message_start.h=set noparent
per-file ipc_message_start.h=file://ipc/SECURITY_OWNERS
+per-file *_messages.cc=set noparent
+per-file *_messages.cc=file://ipc/SECURITY_OWNERS
per-file *_messages*.h=set noparent
per-file *_messages*.h=file://ipc/SECURITY_OWNERS
per-file *.mojom=set noparent
diff --git a/ipc/ipc_cpu_perftest.cc b/ipc/ipc_cpu_perftest.cc
new file mode 100644
index 0000000..976ca1f
--- /dev/null
+++ b/ipc/ipc_cpu_perftest.cc
@@ -0,0 +1,416 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_metrics.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/perf_log.h"
+#include "base/timer/timer.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "ipc/ipc_perftest_util.h"
+#include "ipc/ipc_sync_channel.h"
+#include "ipc/ipc_test.mojom.h"
+#include "ipc/ipc_test_base.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace IPC {
+namespace {
+
+struct TestParams {
+ TestParams() {}
+ TestParams(size_t in_message_size,
+ size_t in_frames_per_second,
+ size_t in_messages_per_frame,
+ size_t in_duration_in_seconds)
+ : message_size(in_message_size),
+ frames_per_second(in_frames_per_second),
+ messages_per_frame(in_messages_per_frame),
+ duration_in_seconds(in_duration_in_seconds) {}
+
+ size_t message_size;
+ size_t frames_per_second;
+ size_t messages_per_frame;
+ size_t duration_in_seconds;
+};
+
+std::vector<TestParams> GetDefaultTestParams() {
+ std::vector<TestParams> list;
+ list.push_back({144, 20, 10, 10});
+ list.push_back({144, 60, 10, 10});
+ return list;
+}
+
+std::string GetLogTitle(const std::string& label, const TestParams& params) {
+ return base::StringPrintf(
+ "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
+ params.message_size, params.frames_per_second, params.messages_per_frame);
+}
+
+base::TimeDelta GetFrameTime(size_t frames_per_second) {
+ return base::TimeDelta::FromSecondsD(1.0 / frames_per_second);
+}
+
+class PerfCpuLogger {
+ public:
+ explicit PerfCpuLogger(base::StringPiece test_name)
+ : test_name_(test_name),
+ process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
+ process_metrics_->GetPlatformIndependentCPUUsage();
+ }
+
+ ~PerfCpuLogger() {
+ double result = process_metrics_->GetPlatformIndependentCPUUsage();
+ base::LogPerfResult(test_name_.c_str(), result, "%");
+ }
+
+ private:
+ std::string test_name_;
+ std::unique_ptr<base::ProcessMetrics> process_metrics_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerfCpuLogger);
+};
+
+MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
+ MojoPerfTestClient client;
+ int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
+ base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
+ true /* pass_pipe_ownership_to_main */);
+
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+
+ return rv;
+}
+
+class ChannelSteadyPingPongListener : public Listener {
+ public:
+ ChannelSteadyPingPongListener() = default;
+
+ ~ChannelSteadyPingPongListener() override = default;
+
+ void Init(Sender* sender) {
+ DCHECK(!sender_);
+ sender_ = sender;
+ }
+
+ void SetTestParams(const TestParams& params,
+ const std::string& label,
+ bool sync,
+ const base::Closure& quit_closure) {
+ params_ = params;
+ label_ = label;
+ sync_ = sync;
+ quit_closure_ = quit_closure;
+ payload_ = std::string(params.message_size, 'a');
+ }
+
+ bool OnMessageReceived(const Message& message) override {
+ CHECK(sender_);
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
+ IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
+ IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void OnHello() {
+ cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
+
+ frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
+
+ timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
+ &ChannelSteadyPingPongListener::StartPingPong);
+ }
+
+ void StartPingPong() {
+ if (sync_) {
+ base::TimeTicks before = base::TimeTicks::Now();
+ for (count_down_ = params_.messages_per_frame; count_down_ > 0;
+ --count_down_) {
+ std::string response;
+ sender_->Send(new TestMsg_SyncPing(payload_, &response));
+ DCHECK_EQ(response, payload_);
+ }
+
+ if (base::TimeTicks::Now() - before >
+ GetFrameTime(params_.frames_per_second)) {
+ LOG(ERROR) << "Frame " << frame_count_down_
+ << " wasn't able to complete on time!";
+ }
+
+ CHECK_GT(frame_count_down_, 0);
+ frame_count_down_--;
+ if (frame_count_down_ == 0)
+ StopPingPong();
+ } else {
+ if (count_down_ != 0) {
+ LOG(ERROR) << "Frame " << frame_count_down_
+ << " wasn't able to complete on time!";
+ } else {
+ SendPong();
+ }
+ count_down_ = params_.messages_per_frame;
+ }
+ }
+
+ void StopPingPong() {
+ cpu_logger_.reset();
+ timer_.AbandonAndStop();
+ quit_closure_.Run();
+ }
+
+ void OnPing(const std::string& payload) {
+ // Include message deserialization in latency.
+ DCHECK_EQ(payload_.size(), payload.size());
+
+ CHECK_GT(count_down_, 0);
+ count_down_--;
+ if (count_down_ > 0) {
+ SendPong();
+ } else {
+ CHECK_GT(frame_count_down_, 0);
+ frame_count_down_--;
+ if (frame_count_down_ == 0)
+ StopPingPong();
+ }
+ }
+
+ void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
+
+ private:
+ Sender* sender_ = nullptr;
+ TestParams params_;
+ std::string payload_;
+ std::string label_;
+ bool sync_ = false;
+
+ int count_down_ = 0;
+ int frame_count_down_ = 0;
+
+ base::RepeatingTimer timer_;
+ std::unique_ptr<PerfCpuLogger> cpu_logger_;
+
+ base::Closure quit_closure_;
+};
+
+class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
+ public:
+ ChannelSteadyPingPongTest() = default;
+ ~ChannelSteadyPingPongTest() override = default;
+
+ void RunPingPongServer(const std::string& label, bool sync) {
+ Init("MojoPerfTestClient");
+
+ // Set up IPC channel and start client.
+ ChannelSteadyPingPongListener listener;
+
+ std::unique_ptr<ChannelProxy> channel_proxy;
+ std::unique_ptr<base::WaitableEvent> shutdown_event;
+
+ if (sync) {
+ shutdown_event = std::make_unique<base::WaitableEvent>(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ channel_proxy = IPC::SyncChannel::Create(
+ TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
+ GetIOThreadTaskRunner(), false, shutdown_event.get());
+ } else {
+ channel_proxy = IPC::ChannelProxy::Create(
+ TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
+ GetIOThreadTaskRunner());
+ }
+ listener.Init(channel_proxy.get());
+
+ LockThreadAffinity thread_locker(kSharedCore);
+ std::vector<TestParams> params_list = GetDefaultTestParams();
+ for (const auto& params : params_list) {
+ base::RunLoop run_loop;
+
+ listener.SetTestParams(params, label, sync,
+ run_loop.QuitWhenIdleClosure());
+
+ // This initial message will kick-start the ping-pong of messages.
+ channel_proxy->Send(new TestMsg_Hello);
+
+ run_loop.Run();
+ }
+
+ // Send quit message.
+ channel_proxy->Send(new TestMsg_Quit);
+
+ EXPECT_TRUE(WaitForClientShutdown());
+ channel_proxy.reset();
+ }
+};
+
+TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
+ RunPingPongServer("IPC_CPU_Async", false);
+}
+
+TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
+ RunPingPongServer("IPC_CPU_Sync", true);
+}
+
+class MojoSteadyPingPongTest : public mojo::edk::test::MojoTestBase {
+ public:
+ MojoSteadyPingPongTest() = default;
+
+ protected:
+ void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
+ label_ = label;
+ sync_ = sync;
+
+ mojo::MessagePipeHandle mp_handle(mp);
+ mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+ ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
+
+ LockThreadAffinity thread_locker(kSharedCore);
+ std::vector<TestParams> params_list = GetDefaultTestParams();
+ for (const auto& params : params_list) {
+ params_ = params;
+ payload_ = std::string(params.message_size, 'a');
+
+ ping_receiver_->Ping("hello", base::Bind(&MojoSteadyPingPongTest::OnHello,
+ base::Unretained(this)));
+ base::RunLoop run_loop;
+ quit_closure_ = run_loop.QuitWhenIdleClosure();
+ run_loop.Run();
+ }
+
+ ping_receiver_->Quit();
+
+ ignore_result(ping_receiver_.PassInterface().PassHandle().release());
+ }
+
+ void OnHello(const std::string& value) {
+ cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
+
+ frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
+
+ timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
+ &MojoSteadyPingPongTest::StartPingPong);
+ }
+
+ void StartPingPong() {
+ if (sync_) {
+ base::TimeTicks before = base::TimeTicks::Now();
+ for (count_down_ = params_.messages_per_frame; count_down_ > 0;
+ --count_down_) {
+ std::string response;
+ ping_receiver_->SyncPing(payload_, &response);
+ DCHECK_EQ(response, payload_);
+ }
+
+ if (base::TimeTicks::Now() - before >
+ GetFrameTime(params_.frames_per_second)) {
+ LOG(ERROR) << "Frame " << frame_count_down_
+ << " wasn't able to complete on time!";
+ }
+
+ CHECK_GT(frame_count_down_, 0);
+ frame_count_down_--;
+ if (frame_count_down_ == 0)
+ StopPingPong();
+ } else {
+ if (count_down_ != 0) {
+ LOG(ERROR) << "Frame " << frame_count_down_
+ << " wasn't able to complete on time!";
+ } else {
+ SendPing();
+ }
+ count_down_ = params_.messages_per_frame;
+ }
+ }
+
+ void StopPingPong() {
+ cpu_logger_.reset();
+ timer_.AbandonAndStop();
+ quit_closure_.Run();
+ }
+
+ void OnPong(const std::string& value) {
+ // Include message deserialization in latency.
+ DCHECK_EQ(payload_.size(), value.size());
+
+ CHECK_GT(count_down_, 0);
+ count_down_--;
+ if (count_down_ > 0) {
+ SendPing();
+ } else {
+ CHECK_GT(frame_count_down_, 0);
+ frame_count_down_--;
+ if (frame_count_down_ == 0)
+ StopPingPong();
+ }
+ }
+
+ void SendPing() {
+ ping_receiver_->Ping(payload_, base::Bind(&MojoSteadyPingPongTest::OnPong,
+ base::Unretained(this)));
+ }
+
+ static int RunPingPongClient(MojoHandle mp) {
+ mojo::MessagePipeHandle mp_handle(mp);
+ mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+
+ LockThreadAffinity thread_locker(kSharedCore);
+ base::RunLoop run_loop;
+ ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
+ run_loop.Run();
+ return 0;
+ }
+
+ private:
+ TestParams params_;
+ std::string payload_;
+ std::string label_;
+ bool sync_ = false;
+
+ IPC::mojom::ReflectorPtr ping_receiver_;
+
+ int count_down_ = 0;
+ int frame_count_down_ = 0;
+
+ base::RepeatingTimer timer_;
+ std::unique_ptr<PerfCpuLogger> cpu_logger_;
+
+ base::Closure quit_closure_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
+};
+
+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
+ base::MessageLoop main_message_loop;
+ return RunPingPongClient(h);
+}
+
+// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
+// instead of raw IPC::Messages.
+TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
+ RunTestClient("PingPongClient", [&](MojoHandle h) {
+ base::MessageLoop main_message_loop;
+ RunPingPongServer(h, "Mojo_CPU_Async", false);
+ });
+}
+
+TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
+ RunTestClient("PingPongClient", [&](MojoHandle h) {
+ base::MessageLoop main_message_loop;
+ RunPingPongServer(h, "Mojo_CPU_Sync", true);
+ });
+}
+
+} // namespace
+} // namespace IPC
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 45f5203..b7cea2a9 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -15,6 +15,8 @@
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "ipc/ipc_channel_mojo.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "ipc/ipc_perftest_util.h"
#include "ipc/ipc_sync_channel.h"
#include "ipc/ipc_test.mojom.h"
#include "ipc/ipc_test_base.h"
@@ -27,25 +29,9 @@
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/system/message_pipe.h"
-#define IPC_MESSAGE_IMPL
-#include "ipc/ipc_message_macros.h"
-
-#define IPC_MESSAGE_START TestMsgStart
-
-IPC_MESSAGE_CONTROL0(TestMsg_Hello)
-IPC_MESSAGE_CONTROL0(TestMsg_Quit)
-IPC_MESSAGE_CONTROL1(TestMsg_Ping, std::string)
-IPC_SYNC_MESSAGE_CONTROL1_1(TestMsg_SyncPing, std::string, std::string)
-
namespace IPC {
namespace {
-scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() {
- scoped_refptr<base::TaskRunner> runner = mojo::edk::GetIOTaskRunner();
- return scoped_refptr<base::SingleThreadTaskRunner>(
- static_cast<base::SingleThreadTaskRunner*>(runner.get()));
-}
-
class PerformanceChannelListener : public Listener {
public:
explicit PerformanceChannelListener(const std::string& label)
@@ -136,102 +122,6 @@
std::unique_ptr<base::PerfTimeLogger> perf_logger_;
};
-// This channel listener just replies to all messages with the exact same
-// message. It assumes each message has one string parameter. When the string
-// "quit" is sent, it will exit.
-class ChannelReflectorListener : public Listener {
- public:
- ChannelReflectorListener() : channel_(NULL) {
- VLOG(1) << "Client listener up";
- }
-
- ~ChannelReflectorListener() override { VLOG(1) << "Client listener down"; }
-
- void Init(Sender* channel) {
- DCHECK(!channel_);
- channel_ = channel;
- }
-
- bool OnMessageReceived(const Message& message) override {
- CHECK(channel_);
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message)
- IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
- IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
- IPC_MESSAGE_HANDLER(TestMsg_SyncPing, OnSyncPing)
- IPC_MESSAGE_HANDLER(TestMsg_Quit, OnQuit)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
- }
-
- void OnHello() { channel_->Send(new TestMsg_Hello); }
-
- void OnPing(const std::string& payload) {
- channel_->Send(new TestMsg_Ping(payload));
- }
-
- void OnSyncPing(const std::string& payload, std::string* response) {
- *response = payload;
- }
-
- void OnQuit() { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }
-
- void Send(IPC::Message* message) { channel_->Send(message); }
-
- private:
- Sender* channel_;
-};
-
-// This class locks the current thread to a particular CPU core. This is
-// important because otherwise the different threads and processes of these
-// tests end up on different CPU cores which means that all of the cores are
-// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
-// frequency, leading to unpredictable and often poor performance.
-class LockThreadAffinity {
- public:
- explicit LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) {
-#if defined(OS_WIN)
- const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
- old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
- affinity_set_ok_ = old_affinity_ != 0;
-#elif defined(OS_LINUX)
- cpu_set_t cpuset;
- CPU_ZERO(&cpuset);
- CPU_SET(cpu_number, &cpuset);
- auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
- DCHECK_EQ(0, get_result);
- auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
- // Check for get_result failure, even though it should always succeed.
- affinity_set_ok_ = (set_result == 0) && (get_result == 0);
-#endif
- if (!affinity_set_ok_)
- LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
- }
-
- ~LockThreadAffinity() {
- if (!affinity_set_ok_)
- return;
-#if defined(OS_WIN)
- auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
- DCHECK_NE(0u, set_result);
-#elif defined(OS_LINUX)
- auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
- DCHECK_EQ(0, set_result);
-#endif
- }
-
- private:
- bool affinity_set_ok_;
-#if defined(OS_WIN)
- DWORD_PTR old_affinity_;
-#elif defined(OS_LINUX)
- cpu_set_t old_cpuset_;
-#endif
-
- DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
-};
-
class PingPongTestParams {
public:
PingPongTestParams(size_t size, int count)
@@ -287,10 +177,6 @@
return list;
}
-// Avoid core 0 due to conflicts with Intel's Power Gadget.
-// Setting thread affinity will fail harmlessly on single/dual core machines.
-const int kSharedCore = 2;
-
class MojoChannelPerfTest : public IPCChannelMojoTestBase {
public:
MojoChannelPerfTest() = default;
@@ -374,34 +260,6 @@
run_loop.RunUntilIdle();
}
-class MojoPerfTestClient {
- public:
- MojoPerfTestClient() : listener_(new ChannelReflectorListener()) {
- mojo::edk::test::MultiprocessTestHelper::ChildSetup();
- }
-
- ~MojoPerfTestClient() = default;
-
- int Run(MojoHandle handle) {
- handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
- LockThreadAffinity thread_locker(kSharedCore);
-
- std::unique_ptr<ChannelProxy> channel =
- IPC::ChannelProxy::Create(handle_.release(), Channel::MODE_CLIENT,
- listener_.get(), GetIOThreadTaskRunner());
- listener_->Init(channel.get());
-
- base::RunLoop().Run();
- return 0;
- }
-
- private:
- base::MessageLoop main_message_loop_;
- std::unique_ptr<ChannelReflectorListener> listener_;
- std::unique_ptr<Channel> channel_;
- mojo::ScopedMessagePipeHandle handle_;
-};
-
MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
MojoPerfTestClient client;
int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
@@ -414,29 +272,6 @@
return rv;
}
-class ReflectorImpl : public IPC::mojom::Reflector {
- public:
- explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle)
- : binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}
- ~ReflectorImpl() override {
- ignore_result(binding_.Unbind().PassMessagePipe().release());
- }
-
- private:
- // IPC::mojom::Reflector:
- void Ping(const std::string& value, PingCallback callback) override {
- std::move(callback).Run(value);
- }
-
- void SyncPing(const std::string& value, PingCallback callback) override {
- std::move(callback).Run(value);
- }
-
- void Quit() override { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }
-
- mojo::Binding<IPC::mojom::Reflector> binding_;
-};
-
class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {
public:
MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}
@@ -509,8 +344,9 @@
base::MessageLoop::current());
LockThreadAffinity thread_locker(kSharedCore);
- ReflectorImpl impl(std::move(scoped_mp));
- base::RunLoop().Run();
+ base::RunLoop run_loop;
+ ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
+ run_loop.Run();
return 0;
}
@@ -798,7 +634,7 @@
mojo::MessagePipeHandle mp_handle(client_handle);
mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
LockThreadAffinity thread_locker(kSharedCore);
- ReflectorImpl impl(std::move(scoped_mp));
+ ReflectorImpl impl(std::move(scoped_mp), base::Closure());
RunPingPongServer(server_handle, "SingleProcess");
}
diff --git a/ipc/ipc_perftest_messages.cc b/ipc/ipc_perftest_messages.cc
new file mode 100644
index 0000000..4a84dae
--- /dev/null
+++ b/ipc/ipc_perftest_messages.cc
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "ipc/ipc_perftest_messages.h"
diff --git a/ipc/ipc_perftest_messages.h b/ipc/ipc_perftest_messages.h
new file mode 100644
index 0000000..1dc0890
--- /dev/null
+++ b/ipc/ipc_perftest_messages.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Multiply-included message header, no traditional include guard.
+
+#include <string>
+
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START TestMsgStart
+
+IPC_MESSAGE_CONTROL0(TestMsg_Hello)
+IPC_MESSAGE_CONTROL0(TestMsg_Quit)
+IPC_MESSAGE_CONTROL1(TestMsg_Ping, std::string)
+IPC_SYNC_MESSAGE_CONTROL1_1(TestMsg_SyncPing, std::string, std::string)
diff --git a/ipc/ipc_perftest_util.cc b/ipc/ipc_perftest_util.cc
new file mode 100644
index 0000000..57f054d
--- /dev/null
+++ b/ipc/ipc_perftest_util.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/ipc_perftest_util.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+
+namespace IPC {
+
+scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() {
+ scoped_refptr<base::TaskRunner> runner = mojo::edk::GetIOTaskRunner();
+ return scoped_refptr<base::SingleThreadTaskRunner>(
+ static_cast<base::SingleThreadTaskRunner*>(runner.get()));
+}
+
+ChannelReflectorListener::ChannelReflectorListener() : channel_(NULL) {
+ VLOG(1) << "Client listener up";
+}
+
+ChannelReflectorListener::~ChannelReflectorListener() {
+ VLOG(1) << "Client listener down";
+}
+
+void ChannelReflectorListener::Init(Sender* channel,
+ const base::Closure& quit_closure) {
+ DCHECK(!channel_);
+ channel_ = channel;
+ quit_closure_ = quit_closure;
+}
+
+bool ChannelReflectorListener::OnMessageReceived(const Message& message) {
+ CHECK(channel_);
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message)
+ IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
+ IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
+ IPC_MESSAGE_HANDLER(TestMsg_SyncPing, OnSyncPing)
+ IPC_MESSAGE_HANDLER(TestMsg_Quit, OnQuit)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void ChannelReflectorListener::OnHello() {
+ channel_->Send(new TestMsg_Hello);
+}
+
+void ChannelReflectorListener::OnPing(const std::string& payload) {
+ channel_->Send(new TestMsg_Ping(payload));
+}
+
+void ChannelReflectorListener::OnSyncPing(const std::string& payload,
+ std::string* response) {
+ *response = payload;
+}
+
+void ChannelReflectorListener::OnQuit() {
+ quit_closure_.Run();
+}
+
+void ChannelReflectorListener::Send(IPC::Message* message) {
+ channel_->Send(message);
+}
+
+LockThreadAffinity::LockThreadAffinity(int cpu_number)
+ : affinity_set_ok_(false) {
+#if defined(OS_WIN)
+ const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
+ old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
+ affinity_set_ok_ = old_affinity_ != 0;
+#elif defined(OS_LINUX)
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu_number, &cpuset);
+ auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+ DCHECK_EQ(0, get_result);
+ auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
+ // Check for get_result failure, even though it should always succeed.
+ affinity_set_ok_ = (set_result == 0) && (get_result == 0);
+#endif
+ if (!affinity_set_ok_)
+ LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
+}
+
+LockThreadAffinity::~LockThreadAffinity() {
+ if (!affinity_set_ok_)
+ return;
+#if defined(OS_WIN)
+ auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
+ DCHECK_NE(0u, set_result);
+#elif defined(OS_LINUX)
+ auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+ DCHECK_EQ(0, set_result);
+#endif
+}
+
+MojoPerfTestClient::MojoPerfTestClient()
+ : listener_(new ChannelReflectorListener()) {
+ mojo::edk::test::MultiprocessTestHelper::ChildSetup();
+}
+
+MojoPerfTestClient::~MojoPerfTestClient() = default;
+
+int MojoPerfTestClient::Run(MojoHandle handle) {
+ handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
+ LockThreadAffinity thread_locker(kSharedCore);
+
+ base::RunLoop run_loop;
+ std::unique_ptr<ChannelProxy> channel =
+ IPC::ChannelProxy::Create(handle_.release(), Channel::MODE_CLIENT,
+ listener_.get(), GetIOThreadTaskRunner());
+ listener_->Init(channel.get(), run_loop.QuitWhenIdleClosure());
+ run_loop.Run();
+ return 0;
+}
+
+ReflectorImpl::ReflectorImpl(mojo::ScopedMessagePipeHandle handle,
+ const base::Closure& quit_closure)
+ : quit_closure_(quit_closure),
+ binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}
+
+ReflectorImpl::~ReflectorImpl() {
+ ignore_result(binding_.Unbind().PassMessagePipe().release());
+}
+
+void ReflectorImpl::Ping(const std::string& value, PingCallback callback) {
+ std::move(callback).Run(value);
+}
+
+void ReflectorImpl::SyncPing(const std::string& value, PingCallback callback) {
+ std::move(callback).Run(value);
+}
+
+void ReflectorImpl::Quit() {
+ if (quit_closure_)
+ quit_closure_.Run();
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_perftest_util.h b/ipc/ipc_perftest_util.h
new file mode 100644
index 0000000..3c9ceeb
--- /dev/null
+++ b/ipc/ipc_perftest_util.h
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPC_IPC_PERFTEST_UTIL_H_
+#define IPC_IPC_PERFTEST_UTIL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_metrics.h"
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/ipc_test.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace IPC {
+
+scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner();
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public Listener {
+ public:
+ ChannelReflectorListener();
+
+ ~ChannelReflectorListener() override;
+
+ void Init(Sender* channel, const base::Closure& quit_closure);
+
+ bool OnMessageReceived(const Message& message) override;
+
+ void OnHello();
+
+ void OnPing(const std::string& payload);
+
+ void OnSyncPing(const std::string& payload, std::string* response);
+
+ void OnQuit();
+
+ void Send(IPC::Message* message);
+
+ private:
+ Sender* channel_;
+ base::Closure quit_closure_;
+};
+
+// This class locks the current thread to a particular CPU core. This is
+// important because otherwise the different threads and processes of these
+// tests end up on different CPU cores which means that all of the cores are
+// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
+// frequency, leading to unpredictable and often poor performance.
+class LockThreadAffinity {
+ public:
+ explicit LockThreadAffinity(int cpu_number);
+
+ ~LockThreadAffinity();
+
+ private:
+ bool affinity_set_ok_;
+#if defined(OS_WIN)
+ DWORD_PTR old_affinity_;
+#elif defined(OS_LINUX)
+ cpu_set_t old_cpuset_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
+};
+
+// Avoid core 0 due to conflicts with Intel's Power Gadget.
+// Setting thread affinity will fail harmlessly on single/dual core machines.
+const int kSharedCore = 2;
+
+class MojoPerfTestClient {
+ public:
+ MojoPerfTestClient();
+
+ ~MojoPerfTestClient();
+
+ int Run(MojoHandle handle);
+
+ private:
+ base::MessageLoop main_message_loop_;
+ std::unique_ptr<ChannelReflectorListener> listener_;
+ std::unique_ptr<Channel> channel_;
+ mojo::ScopedMessagePipeHandle handle_;
+};
+
+class ReflectorImpl : public IPC::mojom::Reflector {
+ public:
+ explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle,
+ const base::Closure& quit_closure);
+
+ ~ReflectorImpl() override;
+
+ private:
+ // IPC::mojom::Reflector:
+ void Ping(const std::string& value, PingCallback callback) override;
+
+ void SyncPing(const std::string& value, PingCallback callback) override;
+
+ void Quit() override;
+
+ base::Closure quit_closure_;
+ mojo::Binding<IPC::mojom::Reflector> binding_;
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_PERFTEST_UTIL_H_