Index: ipc/ipc_mojo_perftest.cc |
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc |
index 0fcb8ba810b6fa99a57cb2ccc38d58415b8a00b8..f327f98073298faaae307700404f0369fa0e45ae 100644 |
--- a/ipc/ipc_mojo_perftest.cc |
+++ b/ipc/ipc_mojo_perftest.cc |
@@ -8,36 +8,408 @@ |
#include "base/memory/ptr_util.h" |
#include "base/process/process_metrics.h" |
#include "base/run_loop.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/test/perf_time_logger.h" |
+#include "base/test/test_io_thread.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "build/build_config.h" |
#include "ipc/ipc_channel_mojo.h" |
-#include "ipc/ipc_perftest_support.h" |
+#include "ipc/ipc_test.mojom.h" |
+#include "ipc/ipc_test_base.h" |
#include "mojo/edk/embedder/embedder.h" |
#include "mojo/edk/embedder/platform_channel_pair.h" |
+#include "mojo/edk/test/mojo_test_base.h" |
#include "mojo/edk/test/multiprocess_test_helper.h" |
#include "mojo/edk/test/scoped_ipc_support.h" |
+#include "mojo/public/cpp/bindings/binding.h" |
+#include "mojo/public/cpp/system/message_pipe.h" |
namespace IPC { |
namespace { |
-class MojoChannelPerfTest : public test::IPCChannelPerfTestBase { |
+// This class simply collects stats about abstract "events" (each of which has a |
+// start time and an end time). |
+class EventTimeTracker { |
public: |
- void TearDown() override { |
- test::IPCChannelPerfTestBase::TearDown(); |
+ explicit EventTimeTracker(const char* name) |
+ : name_(name), |
+ count_(0) { |
} |
- mojo::edk::test::MultiprocessTestHelper helper_; |
+ void AddEvent(const base::TimeTicks& start, const base::TimeTicks& end) { |
+ DCHECK(end >= start); |
+ count_++; |
+ base::TimeDelta duration = end - start; |
+ total_duration_ += duration; |
+ max_duration_ = std::max(max_duration_, duration); |
+ } |
+ |
+ void ShowResults() const { |
+ VLOG(1) << name_ << " count: " << count_; |
+ VLOG(1) << name_ << " total duration: " |
+ << total_duration_.InMillisecondsF() << " ms"; |
+ VLOG(1) << name_ << " average duration: " |
+ << (total_duration_.InMillisecondsF() / static_cast<double>(count_)) |
+ << " ms"; |
+ VLOG(1) << name_ << " maximum duration: " |
+ << max_duration_.InMillisecondsF() << " ms"; |
+ } |
+ |
+ void Reset() { |
+ count_ = 0; |
+ total_duration_ = base::TimeDelta(); |
+ max_duration_ = base::TimeDelta(); |
+ } |
+ |
+ private: |
+ const std::string name_; |
+ |
+ uint64_t count_; |
+ base::TimeDelta total_duration_; |
+ base::TimeDelta max_duration_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(EventTimeTracker); |
+}; |
+ |
+class PerformanceChannelListener : public Listener { |
+ public: |
+ explicit PerformanceChannelListener(const std::string& label) |
+ : label_(label), |
+ sender_(NULL), |
+ msg_count_(0), |
+ msg_size_(0), |
+ count_down_(0), |
+ latency_tracker_("Server messages") { |
+ VLOG(1) << "Server listener up"; |
+ } |
+ |
+ ~PerformanceChannelListener() override { |
+ VLOG(1) << "Server listener down"; |
+ } |
+ |
+ void Init(Sender* sender) { |
+ DCHECK(!sender_); |
+ sender_ = sender; |
+ } |
+ |
+ // Call this before running the message loop. |
+ void SetTestParams(int msg_count, size_t msg_size) { |
+ DCHECK_EQ(0, count_down_); |
+ msg_count_ = msg_count; |
+ msg_size_ = msg_size; |
+ count_down_ = msg_count_; |
+ payload_ = std::string(msg_size_, 'a'); |
+ } |
+ |
+ bool OnMessageReceived(const Message& message) override { |
+ CHECK(sender_); |
+ |
+ base::PickleIterator iter(message); |
+ int64_t time_internal; |
+ EXPECT_TRUE(iter.ReadInt64(&time_internal)); |
+ int msgid; |
+ EXPECT_TRUE(iter.ReadInt(&msgid)); |
+ std::string reflected_payload; |
+ EXPECT_TRUE(iter.ReadString(&reflected_payload)); |
+ |
+ // Include message deserialization in latency. |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ |
+ if (reflected_payload == "hello") { |
+ // Start timing on hello. |
+ latency_tracker_.Reset(); |
+ DCHECK(!perf_logger_.get()); |
+ std::string test_name = |
+ base::StringPrintf("IPC_%s_Perf_%dx_%u", |
+ label_.c_str(), |
+ msg_count_, |
+ static_cast<unsigned>(msg_size_)); |
+ perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str())); |
+ } else { |
+ DCHECK_EQ(payload_.size(), reflected_payload.size()); |
+ |
+ latency_tracker_.AddEvent( |
+ base::TimeTicks::FromInternalValue(time_internal), now); |
+ |
+ CHECK(count_down_ > 0); |
+ count_down_--; |
+ if (count_down_ == 0) { |
+ perf_logger_.reset(); // Stop the perf timer now. |
+ latency_tracker_.ShowResults(); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ return true; |
+ } |
+ } |
+ |
+ Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL); |
+ msg->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ msg->WriteInt(count_down_); |
+ msg->WriteString(payload_); |
+ sender_->Send(msg); |
+ return true; |
+ } |
+ |
+ private: |
+ std::string label_; |
+ Sender* sender_; |
+ int msg_count_; |
+ size_t msg_size_; |
+ |
+ int count_down_; |
+ std::string payload_; |
+ EventTimeTracker latency_tracker_; |
+ 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), |
+ latency_tracker_("Client messages") { |
+ VLOG(1) << "Client listener up"; |
+ } |
+ |
+ ~ChannelReflectorListener() override { |
+ VLOG(1) << "Client listener down"; |
+ latency_tracker_.ShowResults(); |
+ } |
+ |
+ void Init(Channel* channel) { |
+ DCHECK(!channel_); |
+ channel_ = channel; |
+ } |
+ |
+ bool OnMessageReceived(const Message& message) override { |
+ CHECK(channel_); |
+ |
+ base::PickleIterator iter(message); |
+ int64_t time_internal; |
+ EXPECT_TRUE(iter.ReadInt64(&time_internal)); |
+ int msgid; |
+ EXPECT_TRUE(iter.ReadInt(&msgid)); |
+ base::StringPiece payload; |
+ EXPECT_TRUE(iter.ReadStringPiece(&payload)); |
+ |
+ // Include message deserialization in latency. |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ |
+ if (payload == "hello") { |
+ latency_tracker_.Reset(); |
+ } else if (payload == "quit") { |
+ latency_tracker_.ShowResults(); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ return true; |
+ } else { |
+ // Don't track hello and quit messages. |
+ latency_tracker_.AddEvent( |
+ base::TimeTicks::FromInternalValue(time_internal), now); |
+ } |
+ |
+ Message* msg = new Message(0, 2, Message::PRIORITY_NORMAL); |
+ msg->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ msg->WriteInt(msgid); |
+ msg->WriteString(payload); |
+ channel_->Send(msg); |
+ return true; |
+ } |
+ |
+ private: |
+ Channel* channel_; |
+ EventTimeTracker latency_tracker_; |
+}; |
+ |
+// 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) |
+ : message_size_(size), message_count_(count) { |
+ } |
+ |
+ size_t message_size() const { return message_size_; } |
+ int message_count() const { return message_count_; } |
+ |
+ private: |
+ size_t message_size_; |
+ int message_count_; |
+}; |
+ |
+std::vector<PingPongTestParams> GetDefaultTestParams() { |
+ // Test several sizes. We use 12^N for message size, and limit the message |
+ // count to keep the test duration reasonable. |
+#ifdef NDEBUG |
+ const int kMultiplier = 100; |
+#else |
+ // Debug builds on Windows run these tests orders of magnitude more slowly. |
+ const int kMultiplier = 1; |
+#endif |
+ std::vector<PingPongTestParams> list; |
+ list.push_back(PingPongTestParams(12, 500 * kMultiplier)); |
+ list.push_back(PingPongTestParams(144, 500 * kMultiplier)); |
+ list.push_back(PingPongTestParams(1728, 500 * kMultiplier)); |
+ list.push_back(PingPongTestParams(20736, 120 * kMultiplier)); |
+ list.push_back(PingPongTestParams(248832, 10 * kMultiplier)); |
+ 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; |
+ ~MojoChannelPerfTest() override = default; |
+ |
+ void RunTestChannelPingPong() { |
+ Init("MojoPerfTestClient"); |
+ |
+ // Set up IPC channel and start client. |
+ PerformanceChannelListener listener("Channel"); |
+ CreateChannel(&listener); |
+ listener.Init(channel()); |
+ ASSERT_TRUE(ConnectChannel()); |
+ |
+ LockThreadAffinity thread_locker(kSharedCore); |
+ std::vector<PingPongTestParams> params = GetDefaultTestParams(); |
+ for (size_t i = 0; i < params.size(); i++) { |
+ listener.SetTestParams(params[i].message_count(), |
+ params[i].message_size()); |
+ |
+ // This initial message will kick-start the ping-pong of messages. |
+ Message* message = |
+ new Message(0, 2, Message::PRIORITY_NORMAL); |
+ message->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ message->WriteInt(-1); |
+ message->WriteString("hello"); |
+ sender()->Send(message); |
+ |
+ // Run message loop. |
+ base::RunLoop().Run(); |
+ } |
+ |
+ // Send quit message. |
+ Message* message = new Message(0, 2, Message::PRIORITY_NORMAL); |
+ message->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ message->WriteInt(-1); |
+ message->WriteString("quit"); |
+ sender()->Send(message); |
+ |
+ EXPECT_TRUE(WaitForClientShutdown()); |
+ DestroyChannel(); |
+} |
+ |
+ void RunTestChannelProxyPingPong() { |
+ io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart)); |
+ |
+ Init("MojoPerfTestClient"); |
+ |
+ // Set up IPC channel and start client. |
+ PerformanceChannelListener listener("ChannelProxy"); |
+ auto channel_proxy = IPC::ChannelProxy::Create( |
+ TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener, |
+ io_thread_->task_runner()); |
+ listener.Init(channel_proxy.get()); |
+ |
+ LockThreadAffinity thread_locker(kSharedCore); |
+ std::vector<PingPongTestParams> params = GetDefaultTestParams(); |
+ for (size_t i = 0; i < params.size(); i++) { |
+ listener.SetTestParams(params[i].message_count(), |
+ params[i].message_size()); |
+ |
+ // This initial message will kick-start the ping-pong of messages. |
+ Message* message = new Message(0, 2, Message::PRIORITY_NORMAL); |
+ message->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ message->WriteInt(-1); |
+ message->WriteString("hello"); |
+ channel_proxy->Send(message); |
+ |
+ // Run message loop. |
+ base::RunLoop().Run(); |
+ } |
+ |
+ // Send quit message. |
+ Message* message = new Message(0, 2, Message::PRIORITY_NORMAL); |
+ message->WriteInt64(base::TimeTicks::Now().ToInternalValue()); |
+ message->WriteInt(-1); |
+ message->WriteString("quit"); |
+ channel_proxy->Send(message); |
+ |
+ EXPECT_TRUE(WaitForClientShutdown()); |
+ channel_proxy.reset(); |
+ |
+ io_thread_.reset(); |
+ } |
+ |
+ scoped_refptr<base::TaskRunner> io_task_runner() { |
+ if (io_thread_) |
+ return io_thread_->task_runner(); |
+ return base::ThreadTaskRunnerHandle::Get(); |
+ } |
+ |
+ private: |
+ std::unique_ptr<base::TestIOThread> io_thread_; |
}; |
TEST_F(MojoChannelPerfTest, ChannelPingPong) { |
- RunTestChannelPingPong(GetDefaultTestParams()); |
+ RunTestChannelPingPong(); |
base::RunLoop run_loop; |
run_loop.RunUntilIdle(); |
} |
TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) { |
- RunTestChannelProxyPingPong(GetDefaultTestParams()); |
+ RunTestChannelProxyPingPong(); |
base::RunLoop run_loop; |
run_loop.RunUntilIdle(); |
@@ -57,36 +429,34 @@ TEST_F(MojoChannelPerfTest, DISABLED_MaxChannelCount) { |
} |
} |
-class MojoPerfTestClient : public test::PingPongTestClient { |
+class MojoPerfTestClient { |
public: |
- typedef test::PingPongTestClient SuperType; |
+ MojoPerfTestClient() |
+ : listener_(new ChannelReflectorListener()) { |
+ mojo::edk::test::MultiprocessTestHelper::ChildSetup(); |
+ } |
- MojoPerfTestClient(); |
+ ~MojoPerfTestClient() = default; |
- std::unique_ptr<Channel> CreateChannel(Listener* listener) override; |
+ int Run(MojoHandle handle) { |
+ handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle)); |
+ LockThreadAffinity thread_locker(kSharedCore); |
+ std::unique_ptr<Channel> channel = ChannelMojo::Create( |
+ std::move(handle_), Channel::MODE_CLIENT, listener_.get()); |
+ listener_->Init(channel.get()); |
+ CHECK(channel->Connect()); |
- int Run(MojoHandle handle); |
+ base::RunLoop().Run(); |
+ return 0; |
+ } |
private: |
- mojo::edk::test::ScopedIPCSupport ipc_support_; |
+ base::MessageLoopForIO main_message_loop_; |
+ std::unique_ptr<ChannelReflectorListener> listener_; |
+ std::unique_ptr<Channel> channel_; |
mojo::ScopedMessagePipeHandle handle_; |
}; |
-MojoPerfTestClient::MojoPerfTestClient() |
- : ipc_support_(base::ThreadTaskRunnerHandle::Get()) { |
- mojo::edk::test::MultiprocessTestHelper::ChildSetup(); |
-} |
- |
-std::unique_ptr<Channel> MojoPerfTestClient::CreateChannel(Listener* listener) { |
- return ChannelMojo::Create(std::move(handle_), Channel::MODE_CLIENT, |
- listener); |
-} |
- |
-int MojoPerfTestClient::Run(MojoHandle handle) { |
- handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle)); |
- return RunMain(); |
-} |
- |
MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) { |
MojoPerfTestClient client; |
int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain( |
@@ -98,5 +468,208 @@ MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) { |
return rv; |
} |
+class ReflectorImpl : public IPC::mojom::Reflector { |
+ public: |
+ explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle) |
+ : binding_(this, std::move(handle)) {} |
+ ~ReflectorImpl() override { |
+ ignore_result(binding_.Unbind().PassMessagePipe().release()); |
+ } |
+ |
+ private: |
+ // IPC::mojom::Reflector: |
+ void Ping(const std::string& value, const PingCallback& callback) override { |
+ callback.Run(value); |
+ } |
+ |
+ void Quit() override { |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ } |
+ |
+ mojo::Binding<IPC::mojom::Reflector> binding_; |
+}; |
+ |
+class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase { |
+ public: |
+ MojoInterfacePerfTest() : message_count_(0), count_down_(0) {} |
+ |
+ protected: |
+ void RunPingPongServer(MojoHandle mp, const std::string& label) { |
+ base::MessageLoop main_message_loop; |
+ label_ = label; |
+ |
+ 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<PingPongTestParams> params = GetDefaultTestParams(); |
+ for (size_t i = 0; i < params.size(); i++) { |
+ ping_receiver_->Ping( |
+ "hello", |
+ base::Bind(&MojoInterfacePerfTest::OnPong, base::Unretained(this))); |
+ message_count_ = count_down_ = params[i].message_count(); |
+ payload_ = std::string(params[i].message_size(), 'a'); |
+ |
+ base::RunLoop().Run(); |
+ } |
+ |
+ ping_receiver_->Quit(); |
+ |
+ ignore_result(ping_receiver_.PassInterface().PassHandle().release()); |
+ } |
+ |
+ void OnPong(const std::string& value) { |
+ if (value == "hello") { |
+ DCHECK(!perf_logger_.get()); |
+ std::string test_name = |
+ base::StringPrintf("IPC_%s_Perf_%dx_%zu", |
+ label_.c_str(), |
+ message_count_, |
+ payload_.size()); |
+ perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str())); |
+ } else { |
+ DCHECK_EQ(payload_.size(), value.size()); |
+ |
+ CHECK(count_down_ > 0); |
+ count_down_--; |
+ if (count_down_ == 0) { |
+ perf_logger_.reset(); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ return; |
+ } |
+ } |
+ |
+ ping_receiver_->Ping( |
+ payload_, |
+ base::Bind(&MojoInterfacePerfTest::OnPong, base::Unretained(this))); |
+ } |
+ |
+ static int RunPingPongClient(MojoHandle mp) { |
+ mojo::MessagePipeHandle mp_handle(mp); |
+ mojo::ScopedMessagePipeHandle scoped_mp(mp_handle); |
+ |
+ // In single process mode, this is running in a task and by default other |
+ // tasks (in particular, the binding) won't run. To keep the single process |
+ // and multi-process code paths the same, enable nestable tasks. |
+ base::MessageLoop::ScopedNestableTaskAllower nest_loop( |
+ base::MessageLoop::current()); |
+ |
+ LockThreadAffinity thread_locker(kSharedCore); |
+ ReflectorImpl impl(std::move(scoped_mp)); |
+ base::RunLoop().Run(); |
+ return 0; |
+ } |
+ |
+ private: |
+ int message_count_; |
+ int count_down_; |
+ std::string label_; |
+ std::string payload_; |
+ IPC::mojom::ReflectorPtr ping_receiver_; |
+ std::unique_ptr<base::PerfTimeLogger> perf_logger_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MojoInterfacePerfTest); |
+}; |
+ |
+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoInterfacePerfTest, h) { |
+ base::MessageLoop main_message_loop; |
+ return RunPingPongClient(h); |
+} |
+ |
+// Similar to MojoChannelPerfTest above, but uses a Mojo interface instead of |
+// raw IPC::Messages. |
+TEST_F(MojoInterfacePerfTest, MultiprocessPingPong) { |
+ RUN_CHILD_ON_PIPE(PingPongClient, h) |
+ RunPingPongServer(h, "MultiProcess"); |
+ END_CHILD() |
+} |
+ |
+// A single process version of the above test. |
+TEST_F(MojoInterfacePerfTest, SingleProcessPingPong) { |
+ MojoHandle server_handle, client_handle; |
+ CreateMessagePipe(&server_handle, &client_handle); |
+ |
+ base::Thread client_thread("PingPongClient"); |
+ client_thread.Start(); |
+ client_thread.task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle)); |
+ |
+ RunPingPongServer(server_handle, "SingleProcess"); |
+} |
+ |
+class CallbackPerfTest : public testing::Test { |
+ public: |
+ CallbackPerfTest() |
+ : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {} |
+ |
+ protected: |
+ void RunPingPongServer() { |
+ client_thread_.Start(); |
+ |
+ LockThreadAffinity thread_locker(kSharedCore); |
+ std::vector<PingPongTestParams> params = GetDefaultTestParams(); |
+ for (size_t i = 0; i < params.size(); i++) { |
+ std::string hello("hello"); |
+ client_thread_.task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), hello)); |
+ message_count_ = count_down_ = params[i].message_count(); |
+ payload_ = std::string(params[i].message_size(), 'a'); |
+ |
+ base::RunLoop().Run(); |
+ } |
+ } |
+ |
+ void Ping(const std::string& value) { |
+ main_message_loop.task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&CallbackPerfTest::OnPong, base::Unretained(this), |
+ value)); |
+ } |
+ |
+ void OnPong(const std::string& value) { |
+ if (value == "hello") { |
+ DCHECK(!perf_logger_.get()); |
+ std::string test_name = |
+ base::StringPrintf("Callback_Perf_%dx_%zu", |
+ message_count_, |
+ payload_.size()); |
+ perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str())); |
+ } else { |
+ DCHECK_EQ(payload_.size(), value.size()); |
+ |
+ CHECK(count_down_ > 0); |
+ count_down_--; |
+ if (count_down_ == 0) { |
+ perf_logger_.reset(); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ return; |
+ } |
+ } |
+ |
+ client_thread_.task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), payload_)); |
+ } |
+ |
+ private: |
+ base::Thread client_thread_; |
+ base::MessageLoop main_message_loop; |
+ int message_count_; |
+ int count_down_; |
+ std::string payload_; |
+ std::unique_ptr<base::PerfTimeLogger> perf_logger_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CallbackPerfTest); |
+}; |
+ |
+// Sends the same data as above using PostTask instead of IPCs for comparison. |
+TEST_F(CallbackPerfTest, PingPong) { |
+ RunPingPongServer(); |
+} |
+ |
} // namespace |
} // namespace IPC |