// 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/message_loop/message_loop_current.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_time_logger.h"
#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"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/test/mojo_test_base.h"
#include "mojo/core/test/multiprocess_test_helper.h"
#include "mojo/public/cpp/bindings/associated_binding_set.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/system/message_pipe.h"
namespace IPC {
namespace {
class PerformanceChannelListener : public Listener {
public:
explicit PerformanceChannelListener(const std::string& label)
: label_(label),
sender_(NULL),
msg_count_(0),
msg_size_(0),
sync_(false),
count_down_(0) {
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, bool sync) {
DCHECK_EQ(0, count_down_);
msg_count_ = msg_count;
msg_size_ = msg_size;
sync_ = sync;
count_down_ = msg_count_;
payload_ = std::string(msg_size_, 'a');
}
bool OnMessageReceived(const Message& message) override {
CHECK(sender_);
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PerformanceChannelListener, 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() {
// Start timing on hello.
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()));
if (sync_) {
for (; count_down_ > 0; --count_down_) {
std::string response;
sender_->Send(new TestMsg_SyncPing(payload_, &response));
DCHECK_EQ(response, payload_);
}
perf_logger_.reset();
base::RunLoop::QuitCurrentWhenIdleDeprecated();
} else {
SendPong();
}
}
void OnPing(const std::string& payload) {
// Include message deserialization in latency.
DCHECK_EQ(payload_.size(), payload.size());
CHECK(count_down_ > 0);
count_down_--;
if (count_down_ == 0) {
perf_logger_.reset(); // Stop the perf timer now.
base::RunLoop::QuitCurrentWhenIdleDeprecated();
return;
}
SendPong();
}
void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
private:
std::string label_;
Sender* sender_;
int msg_count_;
size_t msg_size_;
bool sync_;
int count_down_;
std::string payload_;
std::unique_ptr<base::PerfTimeLogger> perf_logger_;
};
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_;
};
class InterfacePassingTestParams {
public:
InterfacePassingTestParams(size_t rounds, size_t num_interfaces)
: rounds_(rounds), num_interfaces_(num_interfaces) {}
size_t rounds() const { return rounds_; }
size_t num_interfaces() const { return num_interfaces_; }
private:
size_t rounds_;
size_t num_interfaces_;
};
#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> GetDefaultTestParams() {
// Test several sizes. We use 12^N for message size, and limit the message
// count to keep the test duration reasonable.
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;
}
std::vector<InterfacePassingTestParams> GetDefaultInterfacePassingTestParams() {
std::vector<InterfacePassingTestParams> list;
list.push_back({500 * kMultiplier, 0});
list.push_back({500 * kMultiplier, 1});
list.push_back({500 * kMultiplier, 2});
list.push_back({500 * kMultiplier, 4});
list.push_back({500 * kMultiplier, 8});
return list;
}
class MojoChannelPerfTest : public IPCChannelMojoTestBase {
public:
MojoChannelPerfTest() = default;
~MojoChannelPerfTest() override = default;
void RunTestChannelProxyPingPong() {
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,
GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
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(), false);
// This initial message will kick-start the ping-pong of messages.
channel_proxy->Send(new TestMsg_Hello);
// Run message loop.
base::RunLoop().Run();
}
// Send quit message.
channel_proxy->Send(new TestMsg_Quit);
EXPECT_TRUE(WaitForClientShutdown());
channel_proxy.reset();
}
void RunTestChannelProxySyncPing() {
Init("MojoPerfTestClient");
// Set up IPC channel and start client.
PerformanceChannelListener listener("ChannelProxy");
base::WaitableEvent shutdown_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
auto channel_proxy = IPC::SyncChannel::Create(
TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
&shutdown_event);
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(), true);
// This initial message will kick-start the ping-pong of messages.
channel_proxy->Send(new TestMsg_Hello);
// Run message loop.
base::RunLoop().Run();
}
// Send quit message.
channel_proxy->Send(new TestMsg_Quit);
EXPECT_TRUE(WaitForClientShutdown());
channel_proxy.reset();
}
};
TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) {
RunTestChannelProxyPingPong();
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
TEST_F(MojoChannelPerfTest, ChannelProxySyncPing) {
RunTestChannelProxySyncPing();
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
MojoPerfTestClient client;
int rv = mojo::core::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 MojoInterfacePerfTest : public mojo::core::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::RunLoop::QuitCurrentWhenIdleDeprecated();
return;
}
}
if (sync_) {
for (int i = 0; i < count_down_; ++i) {
std::string response;
ping_receiver_->SyncPing(payload_, &response);
DCHECK_EQ(response, payload_);
}
perf_logger_.reset();
base::RunLoop::QuitCurrentWhenIdleDeprecated();
} else {
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);
LockThreadAffinity thread_locker(kSharedCore);
// 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::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
run_loop.Run();
return 0;
}
bool sync_ = false;
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);
};
class InterfacePassingTestDriverImpl : public mojom::InterfacePassingTestDriver,
public mojom::PingReceiver {
public:
InterfacePassingTestDriverImpl(mojo::ScopedMessagePipeHandle handle,
const base::Closure& quit_closure)
: binding_(this,
mojom::InterfacePassingTestDriverRequest(std::move(handle))),
quit_closure_(quit_closure) {}
~InterfacePassingTestDriverImpl() override {
ignore_result(binding_.Unbind().PassMessagePipe().release());
}
private:
// mojom::InterfacePassingTestDriver implementation:
void Init(InitCallback callback) override { std::move(callback).Run(); }
void GetPingReceiver(std::vector<mojom::PingReceiverRequest> requests,
GetPingReceiverCallback callback) override {
for (auto& request : requests)
ping_receiver_bindings_.AddBinding(this, std::move(request));
ping_receiver_bindings_.CloseAllBindings();
std::move(callback).Run();
}
void GetAssociatedPingReceiver(
std::vector<mojom::PingReceiverAssociatedRequest> requests,
GetAssociatedPingReceiverCallback callback) override {
for (auto& request : requests)
ping_receiver_associated_bindings_.AddBinding(this, std::move(request));
ping_receiver_associated_bindings_.CloseAllBindings();
std::move(callback).Run();
}
void Quit() override {
if (quit_closure_)
quit_closure_.Run();
}
// mojom::PingReceiver implementation:
void Ping(PingCallback callback) override { std::move(callback).Run(); }
mojo::BindingSet<mojom::PingReceiver> ping_receiver_bindings_;
mojo::AssociatedBindingSet<mojom::PingReceiver>
ping_receiver_associated_bindings_;
mojo::Binding<mojom::InterfacePassingTestDriver> binding_;
base::Closure quit_closure_;
};
class MojoInterfacePassingPerfTest : public mojo::core::test::MojoTestBase {
public:
MojoInterfacePassingPerfTest() = default;
protected:
void RunInterfacePassingServer(MojoHandle mp,
const std::string& label,
bool associated) {
label_ = label;
associated_ = associated;
mojo::MessagePipeHandle mp_handle(mp);
mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
driver_ptr_.Bind(
mojom::InterfacePassingTestDriverPtrInfo(std::move(scoped_mp), 0u));
auto params = GetDefaultInterfacePassingTestParams();
LockThreadAffinity thread_locker(kSharedCore);
for (size_t i = 0; i < params.size(); ++i) {
driver_ptr_->Init(
base::Bind(&MojoInterfacePassingPerfTest::OnInitCallback,
base::Unretained(this)));
rounds_ = count_down_ = params[i].rounds();
num_interfaces_ = params[i].num_interfaces();
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitWhenIdleClosure();
run_loop.Run();
}
driver_ptr_->Quit();
ignore_result(driver_ptr_.PassInterface().PassHandle().release());
}
void OnInitCallback() {
DCHECK(!perf_logger_.get());
std::string test_name = base::StringPrintf(
"IPC_%s_Perf_%zux_%zu", label_.c_str(), rounds_, num_interfaces_);
perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
DoNextRound();
}
void DoNextRound() {
if (associated_) {
std::vector<mojom::PingReceiverAssociatedPtr> associated_interfaces(
num_interfaces_);
std::vector<mojom::PingReceiverAssociatedRequest> requests(
num_interfaces_);
for (size_t i = 0; i < num_interfaces_; ++i) {
requests[i] = mojo::MakeRequest(&associated_interfaces[i]);
// Force the interface pointer to do full initialization.
associated_interfaces[i].get();
}
driver_ptr_->GetAssociatedPingReceiver(
std::move(requests),
base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
base::Unretained(this)));
} else {
std::vector<mojom::PingReceiverPtr> interfaces(num_interfaces_);
std::vector<mojom::PingReceiverRequest> requests(num_interfaces_);
for (size_t i = 0; i < num_interfaces_; ++i) {
requests[i] = mojo::MakeRequest(&interfaces[i]);
// Force the interface pointer to do full initialization.
interfaces[i].get();
}
driver_ptr_->GetPingReceiver(
std::move(requests),
base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
base::Unretained(this)));
}
}
void OnGetReceiverCallback() {
CHECK_GT(count_down_, 0u);
count_down_--;
if (count_down_ == 0) {
perf_logger_.reset();
quit_closure_.Run();
return;
}
DoNextRound();
}
static int RunInterfacePassingClient(MojoHandle mp) {
mojo::MessagePipeHandle mp_handle(mp);
mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
LockThreadAffinity thread_locker(kSharedCore);
// 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::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
InterfacePassingTestDriverImpl impl(std::move(scoped_mp),
run_loop.QuitWhenIdleClosure());
run_loop.Run();
return 0;
}
private:
size_t rounds_ = 0;
size_t count_down_ = 0;
size_t num_interfaces_ = 0;
std::string label_;
bool associated_ = false;
std::unique_ptr<base::PerfTimeLogger> perf_logger_;
mojom::InterfacePassingTestDriverPtr driver_ptr_;
base::Closure quit_closure_;
DISALLOW_COPY_AND_ASSIGN(MojoInterfacePassingPerfTest);
};
DEFINE_TEST_CLIENT_WITH_PIPE(InterfacePassingClient,
MojoInterfacePassingPerfTest,
h) {
base::MessageLoop main_message_loop;
return RunInterfacePassingClient(h);
}
enum class InProcessMessageMode {
kSerialized,
kUnserialized,
};
template <class TestBase>
class InProcessPerfTest
: public TestBase,
public testing::WithParamInterface<InProcessMessageMode> {
public:
InProcessPerfTest() {
switch (GetParam()) {
case InProcessMessageMode::kSerialized:
mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
mojo::Connector::OutgoingSerializationMode::kEager,
mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
break;
case InProcessMessageMode::kUnserialized:
mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
mojo::Connector::OutgoingSerializationMode::kLazy,
mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
break;
}
}
};
using MojoInProcessInterfacePerfTest = InProcessPerfTest<MojoInterfacePerfTest>;
using MojoInProcessInterfacePassingPerfTest =
InProcessPerfTest<MojoInterfacePassingPerfTest>;
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) {
RunTestClient("PingPongClient", [&](MojoHandle h) {
base::MessageLoop main_message_loop;
RunPingPongServer(h, "Multiprocess");
});
}
TEST_F(MojoInterfacePerfTest, MultiprocessSyncPing) {
sync_ = true;
RunTestClient("PingPongClient", [&](MojoHandle h) {
base::MessageLoop main_message_loop;
RunPingPongServer(h, "MultiprocessSync");
});
}
TEST_F(MojoInterfacePassingPerfTest, MultiprocessInterfacePassing) {
RunTestClient("InterfacePassingClient", [&](MojoHandle h) {
base::MessageLoop main_message_loop;
RunInterfacePassingServer(h, "InterfacePassing", false /* associated */);
});
}
TEST_F(MojoInterfacePassingPerfTest, MultiprocessAssociatedInterfacePassing) {
RunTestClient("InterfacePassingClient", [&](MojoHandle h) {
base::MessageLoop main_message_loop;
RunInterfacePassingServer(h, "AssociatedInterfacePassing",
true /* associated*/);
});
}
// A single process version of the above test.
TEST_P(MojoInProcessInterfacePerfTest, MultiThreadPingPong) {
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_P(MojoInProcessInterfacePerfTest, SingleThreadPingPong) {
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), base::Closure());
RunPingPongServer(server_handle, "SingleProcess");
}
INSTANTIATE_TEST_CASE_P(,
MojoInProcessInterfacePerfTest,
testing::Values(InProcessMessageMode::kSerialized,
InProcessMessageMode::kUnserialized));
TEST_P(MojoInProcessInterfacePassingPerfTest, MultiThreadInterfacePassing) {
MojoHandle server_handle, client_handle;
CreateMessagePipe(&server_handle, &client_handle);
base::Thread client_thread("InterfacePassingClient");
client_thread.Start();
client_thread.task_runner()->PostTask(
FROM_HERE, base::Bind(base::IgnoreResult(&RunInterfacePassingClient),
client_handle));
base::MessageLoop main_message_loop;
RunInterfacePassingServer(server_handle, "SingleProcess",
false /* associated */);
}
TEST_P(MojoInProcessInterfacePassingPerfTest,
MultiThreadAssociatedInterfacePassing) {
MojoHandle server_handle, client_handle;
CreateMessagePipe(&server_handle, &client_handle);
base::Thread client_thread("InterfacePassingClient");
client_thread.Start();
client_thread.task_runner()->PostTask(
FROM_HERE, base::Bind(base::IgnoreResult(&RunInterfacePassingClient),
client_handle));
base::MessageLoop main_message_loop;
RunInterfacePassingServer(server_handle, "SingleProcess",
true /* associated */);
}
TEST_P(MojoInProcessInterfacePassingPerfTest, SingleThreadInterfacePassing) {
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);
InterfacePassingTestDriverImpl impl(std::move(scoped_mp), base::Closure());
RunInterfacePassingServer(server_handle, "SingleProcess",
false /* associated */);
}
TEST_P(MojoInProcessInterfacePassingPerfTest,
SingleThreadAssociatedInterfacePassing) {
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);
InterfacePassingTestDriverImpl impl(std::move(scoped_mp), base::Closure());
RunInterfacePassingServer(server_handle, "SingleProcess",
true /* associated */);
}
INSTANTIATE_TEST_CASE_P(,
MojoInProcessInterfacePassingPerfTest,
testing::Values(InProcessMessageMode::kSerialized,
InProcessMessageMode::kUnserialized));
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::RunLoop::QuitCurrentWhenIdleDeprecated();
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&, int,
const base::Callback<void(const std::string&, int)>&)>
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_SingleThreadNoPostTask_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_, j,
base::Bind(&CallbackPerfTest::SingleThreadPongNoPostTask,
base::Unretained(this)));
}
perf_logger_.reset();
}
}
void SingleThreadPingNoPostTask(
const std::string& value,
int i,
const base::Callback<void(const std::string&, int)>& pong) {
pong.Run(value, i);
}
void SingleThreadPongNoPostTask(const std::string& value, int i) {}
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::MessageLoopCurrent::Get()->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::MessageLoopCurrent::Get()->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_SingleThreadPostTask_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::RunLoop::QuitCurrentWhenIdleDeprecated();
return;
}
}
base::MessageLoopCurrent::Get()->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