| 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 |