1 // Copyright 2013 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.
4
5 // This tests the performance of the C API.
6
7 #include "mojo/public/c/system/core.h"
8
9 #include <assert.h>
10 #include <stdint.h>
11 #include <stdio.h>
12
13 #include "base/macros.h"
14 #include "base/threading/simple_thread.h"
15 #include "mojo/public/cpp/test_support/test_support.h"
16 #include "mojo/public/cpp/test_support/test_utils.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 #if !defined(WIN32)
20 #include <time.h>
21 #endif // !defined(WIN32)
22
23 namespace {
24
25 #if !defined(WIN32)
26 class MessagePipeWriterThread : public base::SimpleThread {
27 public:
MessagePipeWriterThread(MojoHandle handle,uint32_t num_bytes)28 MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes)
29 : SimpleThread("MessagePipeWriterThread"),
30 handle_(handle),
31 num_bytes_(num_bytes),
32 num_writes_(0) {}
~MessagePipeWriterThread()33 ~MessagePipeWriterThread() override {}
34
Run()35 void Run() override {
36 char buffer[10000];
37 assert(num_bytes_ <= sizeof(buffer));
38
39 // TODO(vtl): Should I throttle somehow?
40 for (;;) {
41 MojoResult result = MojoWriteMessage(handle_, buffer, num_bytes_, nullptr,
42 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
43 if (result == MOJO_RESULT_OK) {
44 num_writes_++;
45 continue;
46 }
47
48 // We failed to write.
49 // Either |handle_| or its peer was closed.
50 assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
51 result == MOJO_RESULT_FAILED_PRECONDITION);
52 break;
53 }
54 }
55
56 // Use only after joining the thread.
num_writes() const57 int64_t num_writes() const { return num_writes_; }
58
59 private:
60 const MojoHandle handle_;
61 const uint32_t num_bytes_;
62 int64_t num_writes_;
63
64 DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread);
65 };
66
67 class MessagePipeReaderThread : public base::SimpleThread {
68 public:
MessagePipeReaderThread(MojoHandle handle)69 explicit MessagePipeReaderThread(MojoHandle handle)
70 : SimpleThread("MessagePipeReaderThread"),
71 handle_(handle),
72 num_reads_(0) {}
~MessagePipeReaderThread()73 ~MessagePipeReaderThread() override {}
74
Run()75 void Run() override {
76 char buffer[10000];
77
78 for (;;) {
79 uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
80 MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, nullptr,
81 nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
82 if (result == MOJO_RESULT_OK) {
83 num_reads_++;
84 continue;
85 }
86
87 if (result == MOJO_RESULT_SHOULD_WAIT) {
88 result = MojoWait(handle_, MOJO_HANDLE_SIGNAL_READABLE,
89 MOJO_DEADLINE_INDEFINITE, nullptr);
90 if (result == MOJO_RESULT_OK) {
91 // Go to the top of the loop to read again.
92 continue;
93 }
94 }
95
96 // We failed to read and possibly failed to wait.
97 // Either |handle_| or its peer was closed.
98 assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
99 result == MOJO_RESULT_FAILED_PRECONDITION);
100 break;
101 }
102 }
103
104 // Use only after joining the thread.
num_reads() const105 int64_t num_reads() const { return num_reads_; }
106
107 private:
108 const MojoHandle handle_;
109 int64_t num_reads_;
110
111 DISALLOW_COPY_AND_ASSIGN(MessagePipeReaderThread);
112 };
113 #endif // !defined(WIN32)
114
115 class CorePerftest : public testing::Test {
116 public:
CorePerftest()117 CorePerftest() : buffer_(nullptr), num_bytes_(0) {}
~CorePerftest()118 ~CorePerftest() override {}
119
NoOp(void *)120 static void NoOp(void* /*closure*/) {}
121
MessagePipe_CreateAndClose(void * closure)122 static void MessagePipe_CreateAndClose(void* closure) {
123 CorePerftest* self = static_cast<CorePerftest*>(closure);
124 MojoResult result = MojoCreateMessagePipe(nullptr, &self->h0_, &self->h1_);
125 ALLOW_UNUSED_LOCAL(result);
126 assert(result == MOJO_RESULT_OK);
127 result = MojoClose(self->h0_);
128 assert(result == MOJO_RESULT_OK);
129 result = MojoClose(self->h1_);
130 assert(result == MOJO_RESULT_OK);
131 }
132
MessagePipe_WriteAndRead(void * closure)133 static void MessagePipe_WriteAndRead(void* closure) {
134 CorePerftest* self = static_cast<CorePerftest*>(closure);
135 MojoResult result =
136 MojoWriteMessage(self->h0_, self->buffer_, self->num_bytes_, nullptr, 0,
137 MOJO_WRITE_MESSAGE_FLAG_NONE);
138 ALLOW_UNUSED_LOCAL(result);
139 assert(result == MOJO_RESULT_OK);
140 uint32_t read_bytes = self->num_bytes_;
141 result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, nullptr,
142 nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
143 assert(result == MOJO_RESULT_OK);
144 }
145
MessagePipe_EmptyRead(void * closure)146 static void MessagePipe_EmptyRead(void* closure) {
147 CorePerftest* self = static_cast<CorePerftest*>(closure);
148 MojoResult result =
149 MojoReadMessage(self->h0_, nullptr, nullptr, nullptr, nullptr,
150 MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
151 ALLOW_UNUSED_LOCAL(result);
152 assert(result == MOJO_RESULT_SHOULD_WAIT);
153 }
154
155 protected:
156 #if !defined(WIN32)
DoMessagePipeThreadedTest(unsigned num_writers,unsigned num_readers,uint32_t num_bytes)157 void DoMessagePipeThreadedTest(unsigned num_writers,
158 unsigned num_readers,
159 uint32_t num_bytes) {
160 static const int64_t kPerftestTimeMicroseconds = 3 * 1000000;
161
162 assert(num_writers > 0);
163 assert(num_readers > 0);
164
165 MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
166 ALLOW_UNUSED_LOCAL(result);
167 assert(result == MOJO_RESULT_OK);
168
169 std::vector<MessagePipeWriterThread*> writers;
170 for (unsigned i = 0; i < num_writers; i++)
171 writers.push_back(new MessagePipeWriterThread(h0_, num_bytes));
172
173 std::vector<MessagePipeReaderThread*> readers;
174 for (unsigned i = 0; i < num_readers; i++)
175 readers.push_back(new MessagePipeReaderThread(h1_));
176
177 // Start time here, just before we fire off the threads.
178 const MojoTimeTicks start_time = MojoGetTimeTicksNow();
179
180 // Interleave the starts.
181 for (unsigned i = 0; i < num_writers || i < num_readers; i++) {
182 if (i < num_writers)
183 writers[i]->Start();
184 if (i < num_readers)
185 readers[i]->Start();
186 }
187
188 Sleep(kPerftestTimeMicroseconds);
189
190 // Close both handles to make writers and readers stop immediately.
191 result = MojoClose(h0_);
192 assert(result == MOJO_RESULT_OK);
193 result = MojoClose(h1_);
194 assert(result == MOJO_RESULT_OK);
195
196 // Join everything.
197 for (unsigned i = 0; i < num_writers; i++)
198 writers[i]->Join();
199 for (unsigned i = 0; i < num_readers; i++)
200 readers[i]->Join();
201
202 // Stop time here.
203 MojoTimeTicks end_time = MojoGetTimeTicksNow();
204
205 // Add up write and read counts, and destroy the threads.
206 int64_t num_writes = 0;
207 for (unsigned i = 0; i < num_writers; i++) {
208 num_writes += writers[i]->num_writes();
209 delete writers[i];
210 }
211 writers.clear();
212 int64_t num_reads = 0;
213 for (unsigned i = 0; i < num_readers; i++) {
214 num_reads += readers[i]->num_reads();
215 delete readers[i];
216 }
217 readers.clear();
218
219 char sub_test_name[200];
220 sprintf(sub_test_name, "%uw_%ur_%ubytes", num_writers, num_readers,
221 static_cast<unsigned>(num_bytes));
222 mojo::test::LogPerfResult(
223 "MessagePipe_Threaded_Writes", sub_test_name,
224 1000000.0 * static_cast<double>(num_writes) / (end_time - start_time),
225 "writes/second");
226 mojo::test::LogPerfResult(
227 "MessagePipe_Threaded_Reads", sub_test_name,
228 1000000.0 * static_cast<double>(num_reads) / (end_time - start_time),
229 "reads/second");
230 }
231 #endif // !defined(WIN32)
232
233 MojoHandle h0_;
234 MojoHandle h1_;
235
236 void* buffer_;
237 uint32_t num_bytes_;
238
239 private:
240 #if !defined(WIN32)
Sleep(int64_t microseconds)241 void Sleep(int64_t microseconds) {
242 struct timespec req = {
243 static_cast<time_t>(microseconds / 1000000), // Seconds.
244 static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds.
245 };
246 int rv = nanosleep(&req, nullptr);
247 ALLOW_UNUSED_LOCAL(rv);
248 assert(rv == 0);
249 }
250 #endif // !defined(WIN32)
251
252 DISALLOW_COPY_AND_ASSIGN(CorePerftest);
253 };
254
255 // A no-op test so we can compare performance.
TEST_F(CorePerftest,NoOp)256 TEST_F(CorePerftest, NoOp) {
257 mojo::test::IterateAndReportPerf("Iterate_NoOp", nullptr, &CorePerftest::NoOp,
258 this);
259 }
260
TEST_F(CorePerftest,MessagePipe_CreateAndClose)261 TEST_F(CorePerftest, MessagePipe_CreateAndClose) {
262 mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", nullptr,
263 &CorePerftest::MessagePipe_CreateAndClose,
264 this);
265 }
266
TEST_F(CorePerftest,MessagePipe_WriteAndRead)267 TEST_F(CorePerftest, MessagePipe_WriteAndRead) {
268 MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
269 ALLOW_UNUSED_LOCAL(result);
270 assert(result == MOJO_RESULT_OK);
271 char buffer[10000] = {0};
272 buffer_ = buffer;
273 num_bytes_ = 10u;
274 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10bytes",
275 &CorePerftest::MessagePipe_WriteAndRead,
276 this);
277 num_bytes_ = 100u;
278 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "100bytes",
279 &CorePerftest::MessagePipe_WriteAndRead,
280 this);
281 num_bytes_ = 1000u;
282 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "1000bytes",
283 &CorePerftest::MessagePipe_WriteAndRead,
284 this);
285 num_bytes_ = 10000u;
286 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10000bytes",
287 &CorePerftest::MessagePipe_WriteAndRead,
288 this);
289 result = MojoClose(h0_);
290 assert(result == MOJO_RESULT_OK);
291 result = MojoClose(h1_);
292 assert(result == MOJO_RESULT_OK);
293 }
294
TEST_F(CorePerftest,MessagePipe_EmptyRead)295 TEST_F(CorePerftest, MessagePipe_EmptyRead) {
296 MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
297 ALLOW_UNUSED_LOCAL(result);
298 assert(result == MOJO_RESULT_OK);
299 mojo::test::IterateAndReportPerf("MessagePipe_EmptyRead", nullptr,
300 &CorePerftest::MessagePipe_EmptyRead, this);
301 result = MojoClose(h0_);
302 assert(result == MOJO_RESULT_OK);
303 result = MojoClose(h1_);
304 assert(result == MOJO_RESULT_OK);
305 }
306
307 #if !defined(WIN32)
TEST_F(CorePerftest,MessagePipe_Threaded)308 TEST_F(CorePerftest, MessagePipe_Threaded) {
309 DoMessagePipeThreadedTest(1u, 1u, 100u);
310 DoMessagePipeThreadedTest(2u, 2u, 100u);
311 DoMessagePipeThreadedTest(3u, 3u, 100u);
312 DoMessagePipeThreadedTest(10u, 10u, 100u);
313 DoMessagePipeThreadedTest(10u, 1u, 100u);
314 DoMessagePipeThreadedTest(1u, 10u, 100u);
315
316 // For comparison of overhead:
317 DoMessagePipeThreadedTest(1u, 1u, 10u);
318 // 100 was done above.
319 DoMessagePipeThreadedTest(1u, 1u, 1000u);
320 DoMessagePipeThreadedTest(1u, 1u, 10000u);
321
322 DoMessagePipeThreadedTest(3u, 3u, 10u);
323 // 100 was done above.
324 DoMessagePipeThreadedTest(3u, 3u, 1000u);
325 DoMessagePipeThreadedTest(3u, 3u, 10000u);
326 }
327 #endif // !defined(WIN32)
328
329 } // namespace
330