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/logging.h"
11 #include "base/macros.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 // Avoid core 0 due to conflicts with Intel's Power Gadget.
30 // Setting thread affinity will fail harmlessly on single/dual core machines.
31 const int kSharedCore
= 2;
33 // This class simply collects stats about abstract "events" (each of which has a
34 // start time and an end time).
35 class EventTimeTracker
{
37 explicit EventTimeTracker(const char* name
)
42 void AddEvent(const base::TimeTicks
& start
, const base::TimeTicks
& end
) {
45 base::TimeDelta duration
= end
- start
;
46 total_duration_
+= duration
;
47 max_duration_
= std::max(max_duration_
, duration
);
50 void ShowResults() const {
51 VLOG(1) << name_
<< " count: " << count_
;
52 VLOG(1) << name_
<< " total duration: "
53 << total_duration_
.InMillisecondsF() << " ms";
54 VLOG(1) << name_
<< " average duration: "
55 << (total_duration_
.InMillisecondsF() / static_cast<double>(count_
))
57 VLOG(1) << name_
<< " maximum duration: "
58 << max_duration_
.InMillisecondsF() << " ms";
63 total_duration_
= base::TimeDelta();
64 max_duration_
= base::TimeDelta();
68 const std::string name_
;
71 base::TimeDelta total_duration_
;
72 base::TimeDelta max_duration_
;
74 DISALLOW_COPY_AND_ASSIGN(EventTimeTracker
);
77 // This channel listener just replies to all messages with the exact same
78 // message. It assumes each message has one string parameter. When the string
79 // "quit" is sent, it will exit.
80 class ChannelReflectorListener
: public Listener
{
82 ChannelReflectorListener()
84 latency_tracker_("Client messages") {
85 VLOG(1) << "Client listener up";
88 ~ChannelReflectorListener() override
{
89 VLOG(1) << "Client listener down";
90 latency_tracker_
.ShowResults();
93 void Init(Channel
* channel
) {
98 bool OnMessageReceived(const Message
& message
) override
{
101 base::PickleIterator
iter(message
);
102 int64_t time_internal
;
103 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
105 EXPECT_TRUE(iter
.ReadInt(&msgid
));
106 base::StringPiece payload
;
107 EXPECT_TRUE(iter
.ReadStringPiece(&payload
));
109 // Include message deserialization in latency.
110 base::TimeTicks now
= base::TimeTicks::Now();
112 if (payload
== "hello") {
113 latency_tracker_
.Reset();
114 } else if (payload
== "quit") {
115 latency_tracker_
.ShowResults();
116 base::MessageLoop::current()->QuitWhenIdle();
119 // Don't track hello and quit messages.
120 latency_tracker_
.AddEvent(
121 base::TimeTicks::FromInternalValue(time_internal
), now
);
124 Message
* msg
= new Message(0, 2, Message::PRIORITY_NORMAL
);
125 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
126 msg
->WriteInt(msgid
);
127 msg
->WriteString(payload
);
134 EventTimeTracker latency_tracker_
;
137 class PerformanceChannelListener
: public Listener
{
139 explicit PerformanceChannelListener(const std::string
& label
)
145 latency_tracker_("Server messages") {
146 VLOG(1) << "Server listener up";
149 ~PerformanceChannelListener() override
{
150 VLOG(1) << "Server listener down";
153 void Init(Sender
* sender
) {
158 // Call this before running the message loop.
159 void SetTestParams(int msg_count
, size_t msg_size
) {
160 DCHECK_EQ(0, count_down_
);
161 msg_count_
= msg_count
;
162 msg_size_
= msg_size
;
163 count_down_
= msg_count_
;
164 payload_
= std::string(msg_size_
, 'a');
167 bool OnMessageReceived(const Message
& message
) override
{
170 base::PickleIterator
iter(message
);
171 int64_t time_internal
;
172 EXPECT_TRUE(iter
.ReadInt64(&time_internal
));
174 EXPECT_TRUE(iter
.ReadInt(&msgid
));
175 std::string reflected_payload
;
176 EXPECT_TRUE(iter
.ReadString(&reflected_payload
));
178 // Include message deserialization in latency.
179 base::TimeTicks now
= base::TimeTicks::Now();
181 if (reflected_payload
== "hello") {
182 // Start timing on hello.
183 latency_tracker_
.Reset();
184 DCHECK(!perf_logger_
.get());
185 std::string test_name
=
186 base::StringPrintf("IPC_%s_Perf_%dx_%u",
189 static_cast<unsigned>(msg_size_
));
190 perf_logger_
.reset(new base::PerfTimeLogger(test_name
.c_str()));
192 DCHECK_EQ(payload_
.size(), reflected_payload
.size());
194 latency_tracker_
.AddEvent(
195 base::TimeTicks::FromInternalValue(time_internal
), now
);
197 CHECK(count_down_
> 0);
199 if (count_down_
== 0) {
200 perf_logger_
.reset(); // Stop the perf timer now.
201 latency_tracker_
.ShowResults();
202 base::MessageLoop::current()->QuitWhenIdle();
207 Message
* msg
= new Message(0, 2, Message::PRIORITY_NORMAL
);
208 msg
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
209 msg
->WriteInt(count_down_
);
210 msg
->WriteString(payload_
);
222 std::string payload_
;
223 EventTimeTracker latency_tracker_
;
224 scoped_ptr
<base::PerfTimeLogger
> perf_logger_
;
227 std::vector
<PingPongTestParams
>
228 IPCChannelPerfTestBase::GetDefaultTestParams() {
229 // Test several sizes. We use 12^N for message size, and limit the message
230 // count to keep the test duration reasonable.
231 std::vector
<PingPongTestParams
> list
;
232 list
.push_back(PingPongTestParams(12, 50000));
233 list
.push_back(PingPongTestParams(144, 50000));
234 list
.push_back(PingPongTestParams(1728, 50000));
235 list
.push_back(PingPongTestParams(20736, 12000));
236 list
.push_back(PingPongTestParams(248832, 1000));
240 void IPCChannelPerfTestBase::RunTestChannelPingPong(
241 const std::vector
<PingPongTestParams
>& params
) {
242 Init("PerformanceClient");
244 // Set up IPC channel and start client.
245 PerformanceChannelListener
listener("Channel");
246 CreateChannel(&listener
);
247 listener
.Init(channel());
248 ASSERT_TRUE(ConnectChannel());
249 ASSERT_TRUE(StartClient());
251 LockThreadAffinity
thread_locker(kSharedCore
);
252 for (size_t i
= 0; i
< params
.size(); i
++) {
253 listener
.SetTestParams(params
[i
].message_count(),
254 params
[i
].message_size());
256 // This initial message will kick-start the ping-pong of messages.
258 new Message(0, 2, Message::PRIORITY_NORMAL
);
259 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
260 message
->WriteInt(-1);
261 message
->WriteString("hello");
262 sender()->Send(message
);
265 base::MessageLoop::current()->Run();
268 // Send quit message.
269 Message
* message
= new Message(0, 2, Message::PRIORITY_NORMAL
);
270 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
271 message
->WriteInt(-1);
272 message
->WriteString("quit");
273 sender()->Send(message
);
275 EXPECT_TRUE(WaitForClientShutdown());
279 void IPCChannelPerfTestBase::RunTestChannelProxyPingPong(
280 const std::vector
<PingPongTestParams
>& params
) {
281 InitWithCustomMessageLoop("PerformanceClient",
282 make_scoped_ptr(new base::MessageLoop()));
284 base::TestIOThread
io_thread(base::TestIOThread::kAutoStart
);
286 // Set up IPC channel and start client.
287 PerformanceChannelListener
listener("ChannelProxy");
288 CreateChannelProxy(&listener
, io_thread
.task_runner());
289 listener
.Init(channel_proxy());
290 ASSERT_TRUE(StartClient());
292 LockThreadAffinity
thread_locker(kSharedCore
);
293 for (size_t i
= 0; i
< params
.size(); i
++) {
294 listener
.SetTestParams(params
[i
].message_count(),
295 params
[i
].message_size());
297 // This initial message will kick-start the ping-pong of messages.
299 new Message(0, 2, Message::PRIORITY_NORMAL
);
300 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
301 message
->WriteInt(-1);
302 message
->WriteString("hello");
303 sender()->Send(message
);
306 base::MessageLoop::current()->Run();
309 // Send quit message.
310 Message
* message
= new Message(0, 2, Message::PRIORITY_NORMAL
);
311 message
->WriteInt64(base::TimeTicks::Now().ToInternalValue());
312 message
->WriteInt(-1);
313 message
->WriteString("quit");
314 sender()->Send(message
);
316 EXPECT_TRUE(WaitForClientShutdown());
317 DestroyChannelProxy();
321 PingPongTestClient::PingPongTestClient()
322 : listener_(new ChannelReflectorListener()) {
325 PingPongTestClient::~PingPongTestClient() {
328 scoped_ptr
<Channel
> PingPongTestClient::CreateChannel(
329 Listener
* listener
) {
330 return Channel::CreateClient(IPCTestBase::GetChannelName("PerformanceClient"),
334 int PingPongTestClient::RunMain() {
335 LockThreadAffinity
thread_locker(kSharedCore
);
336 scoped_ptr
<Channel
> channel
= CreateChannel(listener_
.get());
337 listener_
->Init(channel
.get());
338 CHECK(channel
->Connect());
340 base::MessageLoop::current()->Run();
344 scoped_refptr
<base::TaskRunner
> PingPongTestClient::task_runner() {
345 return main_message_loop_
.task_runner();
348 LockThreadAffinity::LockThreadAffinity(int cpu_number
)
349 : affinity_set_ok_(false) {
351 const DWORD_PTR thread_mask
= 1 << cpu_number
;
352 old_affinity_
= SetThreadAffinityMask(GetCurrentThread(), thread_mask
);
353 affinity_set_ok_
= old_affinity_
!= 0;
354 #elif defined(OS_LINUX)
357 CPU_SET(cpu_number
, &cpuset
);
358 auto get_result
= sched_getaffinity(0, sizeof(old_cpuset_
), &old_cpuset_
);
359 DCHECK_EQ(0, get_result
);
360 auto set_result
= sched_setaffinity(0, sizeof(cpuset
), &cpuset
);
361 // Check for get_result failure, even though it should always succeed.
362 affinity_set_ok_
= (set_result
== 0) && (get_result
== 0);
364 if (!affinity_set_ok_
)
365 LOG(WARNING
) << "Failed to set thread affinity to CPU " << cpu_number
;
368 LockThreadAffinity::~LockThreadAffinity() {
369 if (!affinity_set_ok_
)
372 auto set_result
= SetThreadAffinityMask(GetCurrentThread(), old_affinity_
);
373 DCHECK_NE(0u, set_result
);
374 #elif defined(OS_LINUX)
375 auto set_result
= sched_setaffinity(0, sizeof(old_cpuset_
), &old_cpuset_
);
376 DCHECK_EQ(0, set_result
);