| // Copyright 2014 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 <stddef.h> |
| #include <memory> |
| |
| #include "base/memory/ptr_util.h" |
| #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/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_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/public/cpp/bindings/binding.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| |
| namespace IPC { |
| namespace { |
| |
| // This class simply collects stats about abstract "events" (each of which has a |
| // start time and an end time). |
| class EventTimeTracker { |
| public: |
| explicit EventTimeTracker(const char* name) |
| : name_(name), |
| count_(0) { |
| } |
| |
| 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(); |
| |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| } |
| |
| TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) { |
| RunTestChannelProxyPingPong(); |
| |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| } |
| |
| // Test to see how many channels we can create. |
| TEST_F(MojoChannelPerfTest, DISABLED_MaxChannelCount) { |
| #if defined(OS_POSIX) |
| LOG(INFO) << "base::GetMaxFds " << base::GetMaxFds(); |
| base::SetFdLimit(20000); |
| #endif |
| |
| std::vector<mojo::edk::PlatformChannelPair*> channels; |
| for (size_t i = 0; i < 10000; ++i) { |
| LOG(INFO) << "channels size: " << channels.size(); |
| channels.push_back(new mojo::edk::PlatformChannelPair()); |
| } |
| } |
| |
| 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<Channel> channel = ChannelMojo::Create( |
| std::move(handle_), Channel::MODE_CLIENT, listener_.get()); |
| listener_->Init(channel.get()); |
| CHECK(channel->Connect()); |
| |
| base::RunLoop().Run(); |
| return 0; |
| } |
| |
| private: |
| base::MessageLoopForIO 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( |
| base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)), |
| true /* pass_pipe_ownership_to_main */); |
| |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| |
| 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, 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) { |
| 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) |
| base::MessageLoop main_message_loop; |
| RunPingPongServer(h, "MultiProcess"); |
| END_CHILD() |
| } |
| |
| // A single process version of the above test. |
| TEST_F(MojoInterfacePerfTest, SingleProcessMultiThreadPingPong) { |
| 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)); |
| |
| base::MessageLoop main_message_loop; |
| RunPingPongServer(server_handle, "SingleProcess"); |
| } |
| |
| TEST_F(MojoInterfacePerfTest, SingleProcessSingleThreadPingPong) { |
| MojoHandle server_handle, client_handle; |
| CreateMessagePipe(&server_handle, &client_handle); |
| |
| base::MessageLoop main_message_loop; |
| mojo::MessagePipeHandle mp_handle(client_handle); |
| mojo::ScopedMessagePipeHandle scoped_mp(mp_handle); |
| LockThreadAffinity thread_locker(kSharedCore); |
| ReflectorImpl impl(std::move(scoped_mp)); |
| |
| RunPingPongServer(server_handle, "SingleProcess"); |
| } |
| |
| class CallbackPerfTest : public testing::Test { |
| public: |
| CallbackPerfTest() |
| : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {} |
| |
| protected: |
| void RunMultiThreadPingPongServer() { |
| 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_MultiProcess_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_)); |
| } |
| |
| void RunSingleThreadNoPostTaskPingPongServer() { |
| LockThreadAffinity thread_locker(kSharedCore); |
| std::vector<PingPongTestParams> params = GetDefaultTestParams(); |
| base::Callback<void(const std::string&, |
| const base::Callback<void(const std::string&)>&)> ping = |
| base::Bind(&CallbackPerfTest::SingleThreadPingNoPostTask, |
| base::Unretained(this)); |
| for (size_t i = 0; i < params.size(); i++) { |
| payload_ = std::string(params[i].message_size(), 'a'); |
| std::string test_name = |
| base::StringPrintf("Callback_SingleThreadPostTask_Perf_%dx_%zu", |
| params[i].message_count(), |
| payload_.size()); |
| perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str())); |
| for (int j = 0; j < params[i].message_count(); ++j) { |
| ping.Run(payload_, |
| base::Bind(&CallbackPerfTest::SingleThreadPongNoPostTask, |
| base::Unretained(this))); |
| } |
| perf_logger_.reset(); |
| } |
| } |
| |
| void SingleThreadPingNoPostTask(const std::string& value, |
| const base::Callback<void(const std::string&)>& pong) { |
| pong.Run(value); |
| } |
| |
| void SingleThreadPongNoPostTask(const std::string& value) { |
| } |
| |
| void RunSingleThreadPostTaskPingPongServer() { |
| LockThreadAffinity thread_locker(kSharedCore); |
| std::vector<PingPongTestParams> params = GetDefaultTestParams(); |
| for (size_t i = 0; i < params.size(); i++) { |
| std::string hello("hello"); |
| base::MessageLoop::current()->task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CallbackPerfTest::SingleThreadPingPostTask, |
| base::Unretained(this), hello)); |
| message_count_ = count_down_ = params[i].message_count(); |
| payload_ = std::string(params[i].message_size(), 'a'); |
| |
| base::RunLoop().Run(); |
| } |
| } |
| |
| void SingleThreadPingPostTask(const std::string& value) { |
| base::MessageLoop::current()->task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CallbackPerfTest::SingleThreadPongPostTask, |
| base::Unretained(this), |
| value)); |
| } |
| |
| void SingleThreadPongPostTask(const std::string& value) { |
| if (value == "hello") { |
| DCHECK(!perf_logger_.get()); |
| std::string test_name = |
| base::StringPrintf("Callback_SingleThreadNoPostTask_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; |
| } |
| } |
| |
| base::MessageLoop::current()->task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CallbackPerfTest::SingleThreadPingPostTask, |
| 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 to a different thread instead of |
| // IPCs for comparison. |
| TEST_F(CallbackPerfTest, MultiThreadPingPong) { |
| RunMultiThreadPingPongServer(); |
| } |
| |
| // Sends the same data as above using PostTask to the same thread. |
| TEST_F(CallbackPerfTest, SingleThreadPostTaskPingPong) { |
| RunSingleThreadPostTaskPingPongServer(); |
| } |
| |
| // Sends the same data as above without using PostTask to the same thread. |
| TEST_F(CallbackPerfTest, SingleThreadNoPostTaskPingPong) { |
| RunSingleThreadNoPostTaskPingPongServer(); |
| } |
| |
| } // namespace |
| } // namespace IPC |