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