Skip to content

Commit

Permalink
Add ipc_mojo_perftests
Browse files Browse the repository at this point in the history
This change adds ipc_mojo_perftests that runs the same benchmark
as of ipc_perftests. Now head-to-head comparison becomes possible.

For this change, whole ipc_perftests logic is extracted to
ipc_perftest_support.cc to make it reusable by ipc_mojo_perftests.

TEST=none
BUG=none
R=jam@chromium.org, darin@chromium.org, yuzhu@chromium.org

Review URL: https://codereview.chromium.org/536213002

Cr-Commit-Position: refs/heads/master@{#293988}
  • Loading branch information
omo authored and Commit bot committed Sep 9, 2014
1 parent 182520b commit 373af03
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 370 deletions.
2 changes: 2 additions & 0 deletions ipc/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ static_library("test_support") {
sources = [
"ipc_multiprocess_test.cc",
"ipc_multiprocess_test.h",
"ipc_perftest_support.cc",
"ipc_perftest_support.h",
"ipc_test_sink.cc",
"ipc_test_sink.h",
"ipc_test_base.cc",
Expand Down
2 changes: 2 additions & 0 deletions ipc/ipc.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@
'sources': [
'ipc_multiprocess_test.cc',
'ipc_multiprocess_test.h',
'ipc_perftest_support.cc',
'ipc_perftest_support.h',
'ipc_test_base.cc',
'ipc_test_base.h',
'ipc_test_channel_listener.cc',
Expand Down
2 changes: 1 addition & 1 deletion ipc/ipc_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class IPC_EXPORT Channel : public Sender {
// TODO(morrita): Replace CreateByModeForProxy() with one of above Create*().
//
static scoped_ptr<Channel> Create(
const IPC::ChannelHandle &channel_handle, Mode mode,Listener* listener);
const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener);

static scoped_ptr<Channel> CreateClient(
const IPC::ChannelHandle &channel_handle, Listener* listener);
Expand Down
2 changes: 1 addition & 1 deletion ipc/ipc_channel_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace IPC {
// Encapsulates how a Channel is created. A ChannelFactory can be
// passed to the constructor of ChannelProxy or SyncChannel to tell them
// how to create underlying channel.
class ChannelFactory {
class IPC_EXPORT ChannelFactory {
public:
// Creates a factory for "native" channel built through
// IPC::Channel::Create().
Expand Down
342 changes: 342 additions & 0 deletions ipc/ipc_perftest_support.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
// Copyright (c) 2012 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_support.h"

#include <algorithm>
#include <string>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/pickle.h"
#include "base/strings/stringprintf.h"
#include "base/test/perf_time_logger.h"
#include "base/test/test_io_thread.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_descriptors.h"
#include "ipc/ipc_message_utils.h"
#include "ipc/ipc_sender.h"

namespace IPC {
namespace test {

// 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 count_;
base::TimeDelta total_duration_;
base::TimeDelta max_duration_;

DISALLOW_COPY_AND_ASSIGN(EventTimeTracker);
};

// 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";
}

virtual ~ChannelReflectorListener() {
VLOG(1) << "Client listener down";
latency_tracker_.ShowResults();
}

void Init(Channel* channel) {
DCHECK(!channel_);
channel_ = channel;
}

virtual bool OnMessageReceived(const Message& message) OVERRIDE {
CHECK(channel_);

PickleIterator iter(message);
int64 time_internal;
EXPECT_TRUE(iter.ReadInt64(&time_internal));
int msgid;
EXPECT_TRUE(iter.ReadInt(&msgid));
std::string payload;
EXPECT_TRUE(iter.ReadString(&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_;
};

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";
}

virtual ~PerformanceChannelListener() {
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');
}

virtual bool OnMessageReceived(const Message& message) OVERRIDE {
CHECK(sender_);

PickleIterator iter(message);
int64 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_;
scoped_ptr<base::PerfTimeLogger> perf_logger_;
};

std::vector<PingPongTestParams>
IPCChannelPerfTestBase::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, 50000));
list.push_back(PingPongTestParams(144, 50000));
list.push_back(PingPongTestParams(1728, 50000));
list.push_back(PingPongTestParams(20736, 12000));
list.push_back(PingPongTestParams(248832, 100));
return list;
}

void IPCChannelPerfTestBase::RunTestChannelPingPong(
const std::vector<PingPongTestParams>& params) {
Init("PerformanceClient");

// Set up IPC channel and start client.
PerformanceChannelListener listener("Channel");
CreateChannel(&listener);
listener.Init(channel());
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());

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::MessageLoop::current()->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 IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
const std::vector<PingPongTestParams>& params) {
InitWithCustomMessageLoop("PerformanceClient",
make_scoped_ptr(new base::MessageLoop()));

base::TestIOThread io_thread(base::TestIOThread::kAutoStart);

// Set up IPC channel and start client.
PerformanceChannelListener listener("ChannelProxy");
CreateChannelProxy(&listener, io_thread.task_runner());
listener.Init(channel_proxy());
ASSERT_TRUE(StartClient());

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::MessageLoop::current()->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());
DestroyChannelProxy();
}


PingPongTestClient::PingPongTestClient()
: listener_(new ChannelReflectorListener()) {
}

PingPongTestClient::~PingPongTestClient() {
}

scoped_ptr<Channel> PingPongTestClient::CreateChannel(
Listener* listener) {
return Channel::CreateClient(
IPCTestBase::GetChannelName("PerformanceClient"), listener);
}

int PingPongTestClient::RunMain() {
scoped_ptr<Channel> channel = CreateChannel(listener_.get());
listener_->Init(channel.get());
CHECK(channel->Connect());

base::MessageLoop::current()->Run();
return 0;
}

scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() {
return main_message_loop_.message_loop_proxy();
}

} // namespace test
} // namespace IPC
Loading

0 comments on commit 373af03

Please sign in to comment.