1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ipc/ipc_perftest_support.h"
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/pickle.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/perf_time_logger.h"
16 #include "base/test/test_io_thread.h"
17 #include "base/threading/thread.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "ipc/ipc_channel.h"
21 #include "ipc/ipc_channel_proxy.h"
22 #include "ipc/ipc_descriptors.h"
23 #include "ipc/ipc_message_utils.h"
24 #include "ipc/ipc_sender.h"
29 // This class simply collects stats about abstract "events" (each of which has a
30 // start time and an end time).
31 class EventTimeTracker
{
33 explicit EventTimeTracker(const char* name
)
38 void AddEvent(const base::TimeTicks
& start
, const base::TimeTicks
& end
) {
41 base::TimeDelta duration
= end
- start
;
42 total_duration_
+= duration
;
43 max_duration_
= std::max(max_duration_
, duration
);
46 void ShowResults() const {
47 VLOG(1) << name_
<< " count: " << count_
;
48 VLOG(1) << name_
<< " total duration: "
49 << total_duration_
.InMillisecondsF() << " ms";
50 VLOG(1) << name_
<< " average duration: "
51 << (total_duration_
.InMillisecondsF() / static_cast<double>(count_
))
53 VLOG(1) << name_
<< " maximum duration: "
54 << max_duration_
.InMillisecondsF() << " ms";
59 total_duration_
= base::TimeDelta();
60 max_duration_
= base::TimeDelta();
64 const std::string name_
;
67 base::TimeDelta total_duration_
;
68 base::TimeDelta max_duration_
;
70 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker
);
73 // This channel listener just replies to all messages with the exact same
74 // message. It assumes each message has one string parameter. When the string
75 // "quit" is sent, it will exit.
76 class ChannelReflectorListener
: public Listener
{
78 ChannelReflectorListener()
80 latency_tracker_("Client messages") {
81 VLOG(1) << "Client listener up";
84 virtual ~ChannelReflectorListener() {
85 VLOG(1) << "Client listener down";
86 latency_tracker_
.ShowResults();
89 void Init(Channel
* channel
) {
94 virtual bool OnMessageReceived(const Message
& message
) OVERRIDE
{
97 PickleIterator
iter(message
);
99 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
101 EXPECT_TRUE(iter
.ReadInt(&msgid
));
103 EXPECT_TRUE(iter
.ReadString(&payload
));
105 // Include message deserialization in latency.
106 base::TimeTicks now
= base::TimeTicks::Now();
108 if (payload
== "hello") {
109 latency_tracker_
.Reset();
110 } else if (payload
== "quit") {
111 latency_tracker_
.ShowResults();
112 base::MessageLoop::current()->QuitWhenIdle();
115 // Don't track hello and quit messages.
116 latency_tracker_
.AddEvent(
117 base::TimeTicks::FromInternalValue(time_internal
), now
);
120 Message
* msg
= new Message(0, 2, Message::PRIORITY_NORMAL
);
121 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
122 msg
->WriteInt(msgid
);
123 msg
->WriteString(payload
);
130 EventTimeTracker latency_tracker_
;
133 class PerformanceChannelListener
: public Listener
{
135 explicit PerformanceChannelListener(const std::string
& label
)
141 latency_tracker_("Server messages") {
142 VLOG(1) << "Server listener up";
145 virtual ~PerformanceChannelListener() {
146 VLOG(1) << "Server listener down";
149 void Init(Sender
* sender
) {
154 // Call this before running the message loop.
155 void SetTestParams(int msg_count
, size_t msg_size
) {
156 DCHECK_EQ(0, count_down_
);
157 msg_count_
= msg_count
;
158 msg_size_
= msg_size
;
159 count_down_
= msg_count_
;
160 payload_
= std::string(msg_size_
, 'a');
163 virtual bool OnMessageReceived(const Message
& message
) OVERRIDE
{
166 PickleIterator
iter(message
);
168 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
170 EXPECT_TRUE(iter
.ReadInt(&msgid
));
171 std::string reflected_payload
;
172 EXPECT_TRUE(iter
.ReadString(&reflected_payload
));
174 // Include message deserialization in latency.
175 base::TimeTicks now
= base::TimeTicks::Now();
177 if (reflected_payload
== "hello") {
178 // Start timing on hello.
179 latency_tracker_
.Reset();
180 DCHECK(!perf_logger_
.get());
181 std::string test_name
=
182 base::StringPrintf("IPC_%s_Perf_%dx_%u",
185 static_cast<unsigned>(msg_size_
));
186 perf_logger_
.reset(new base::PerfTimeLogger(test_name
.c_str()));
188 DCHECK_EQ(payload_
.size(), reflected_payload
.size());
190 latency_tracker_
.AddEvent(
191 base::TimeTicks::FromInternalValue(time_internal
), now
);
193 CHECK(count_down_
> 0);
195 if (count_down_
== 0) {
196 perf_logger_
.reset(); // Stop the perf timer now.
197 latency_tracker_
.ShowResults();
198 base::MessageLoop::current()->QuitWhenIdle();
203 Message
* msg
= new Message(0, 2, Message::PRIORITY_NORMAL
);
204 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
205 msg
->WriteInt(count_down_
);
206 msg
->WriteString(payload_
);
218 std::string payload_
;
219 EventTimeTracker latency_tracker_
;
220 scoped_ptr
<base::PerfTimeLogger
> perf_logger_
;
223 std::vector
<PingPongTestParams
>
224 IPCChannelPerfTestBase::GetDefaultTestParams() {
225 // Test several sizes. We use 12^N for message size, and limit the message
226 // count to keep the test duration reasonable.
227 std::vector
<PingPongTestParams
> list
;
228 list
.push_back(PingPongTestParams(12, 50000));
229 list
.push_back(PingPongTestParams(144, 50000));
230 list
.push_back(PingPongTestParams(1728, 50000));
231 list
.push_back(PingPongTestParams(20736, 12000));
232 list
.push_back(PingPongTestParams(248832, 100));
236 void IPCChannelPerfTestBase::RunTestChannelPingPong(
237 const std::vector
<PingPongTestParams
>& params
) {
238 Init("PerformanceClient");
240 // Set up IPC channel and start client.
241 PerformanceChannelListener
listener("Channel");
242 CreateChannel(&listener
);
243 listener
.Init(channel());
244 ASSERT_TRUE(ConnectChannel());
245 ASSERT_TRUE(StartClient());
247 for (size_t i
= 0; i
< params
.size(); i
++) {
248 listener
.SetTestParams(params
[i
].message_count(),
249 params
[i
].message_size());
251 // This initial message will kick-start the ping-pong of messages.
253 new Message(0, 2, Message::PRIORITY_NORMAL
);
254 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
255 message
->WriteInt(-1);
256 message
->WriteString("hello");
257 sender()->Send(message
);
260 base::MessageLoop::current()->Run();
263 // Send quit message.
264 Message
* message
= new Message(0, 2, Message::PRIORITY_NORMAL
);
265 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
266 message
->WriteInt(-1);
267 message
->WriteString("quit");
268 sender()->Send(message
);
270 EXPECT_TRUE(WaitForClientShutdown());
274 void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
275 const std::vector
<PingPongTestParams
>& params
) {
276 InitWithCustomMessageLoop("PerformanceClient",
277 make_scoped_ptr(new base::MessageLoop()));
279 base::TestIOThread
io_thread(base::TestIOThread::kAutoStart
);
281 // Set up IPC channel and start client.
282 PerformanceChannelListener
listener("ChannelProxy");
283 CreateChannelProxy(&listener
, io_thread
.task_runner());
284 listener
.Init(channel_proxy());
285 ASSERT_TRUE(StartClient());
287 for (size_t i
= 0; i
< params
.size(); i
++) {
288 listener
.SetTestParams(params
[i
].message_count(),
289 params
[i
].message_size());
291 // This initial message will kick-start the ping-pong of messages.
293 new Message(0, 2, Message::PRIORITY_NORMAL
);
294 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
295 message
->WriteInt(-1);
296 message
->WriteString("hello");
297 sender()->Send(message
);
300 base::MessageLoop::current()->Run();
303 // Send quit message.
304 Message
* message
= new Message(0, 2, Message::PRIORITY_NORMAL
);
305 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
306 message
->WriteInt(-1);
307 message
->WriteString("quit");
308 sender()->Send(message
);
310 EXPECT_TRUE(WaitForClientShutdown());
311 DestroyChannelProxy();
315 PingPongTestClient::PingPongTestClient()
316 : listener_(new ChannelReflectorListener()) {
319 PingPongTestClient::~PingPongTestClient() {
322 scoped_ptr
<Channel
> PingPongTestClient::CreateChannel(
323 Listener
* listener
) {
324 return Channel::CreateClient(
325 IPCTestBase::GetChannelName("PerformanceClient"), listener
);
328 int PingPongTestClient::RunMain() {
329 scoped_ptr
<Channel
> channel
= CreateChannel(listener_
.get());
330 listener_
->Init(channel
.get());
331 CHECK(channel
->Connect());
333 base::MessageLoop::current()->Run();
337 scoped_refptr
<base::TaskRunner
> PingPongTestClient::task_runner() {
338 return main_message_loop_
.message_loop_proxy();