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/system/message_pipe.h" 16 #include "mojo/public/cpp/system/wait.h" 17 #include "mojo/public/cpp/test_support/test_support.h" 18 #include "mojo/public/cpp/test_support/test_utils.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 #if !defined(WIN32) 22 #include <time.h> 23 #endif // !defined(WIN32) 24 25 namespace { 26 27 #if !defined(WIN32) 28 class MessagePipeWriterThread : public base::SimpleThread { 29 public: 30 MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes) 31 : SimpleThread("MessagePipeWriterThread"), 32 handle_(handle), 33 num_bytes_(num_bytes), 34 num_writes_(0) {} 35 ~MessagePipeWriterThread() override {} 36 37 void Run() override { 38 char buffer[10000]; 39 assert(num_bytes_ <= sizeof(buffer)); 40 41 for (;;) { 42 MojoResult result = mojo::WriteMessageRaw( 43 mojo::MessagePipeHandle(handle_), buffer, num_bytes_, nullptr, 0, 44 MOJO_WRITE_MESSAGE_FLAG_NONE); 45 if (result == MOJO_RESULT_OK) { 46 num_writes_++; 47 continue; 48 } 49 50 // We failed to write. 51 // Either |handle_| or its peer was closed. 52 assert(result == MOJO_RESULT_INVALID_ARGUMENT || 53 result == MOJO_RESULT_FAILED_PRECONDITION); 54 break; 55 } 56 } 57 58 // Use only after joining the thread. 59 int64_t num_writes() const { return num_writes_; } 60 61 private: 62 const MojoHandle handle_; 63 const uint32_t num_bytes_; 64 int64_t num_writes_; 65 66 DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread); 67 }; 68 69 class MessagePipeReaderThread : public base::SimpleThread { 70 public: 71 explicit MessagePipeReaderThread(MojoHandle handle) 72 : SimpleThread("MessagePipeReaderThread"), 73 handle_(handle), 74 num_reads_(0) {} 75 ~MessagePipeReaderThread() override {} 76 77 void Run() override { 78 for (;;) { 79 std::vector<uint8_t> bytes; 80 MojoResult result = 81 mojo::ReadMessageRaw(mojo::MessagePipeHandle(handle_), &bytes, 82 nullptr, MOJO_READ_MESSAGE_FLAG_NONE); 83 if (result == MOJO_RESULT_OK) { 84 num_reads_++; 85 continue; 86 } 87 88 if (result == MOJO_RESULT_SHOULD_WAIT) { 89 result = mojo::Wait(mojo::Handle(handle_), MOJO_HANDLE_SIGNAL_READABLE); 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. 105 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: 117 CorePerftest() {} 118 ~CorePerftest() override {} 119 120 static void NoOp(void* /*closure*/) {} 121 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 133 static void MessagePipe_WriteAndRead(void* closure) { 134 CorePerftest* self = static_cast<CorePerftest*>(closure); 135 MojoResult result = mojo::WriteMessageRaw( 136 mojo::MessagePipeHandle(self->h0_), self->buffer_.data(), 137 self->buffer_.size(), nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); 138 ALLOW_UNUSED_LOCAL(result); 139 assert(result == MOJO_RESULT_OK); 140 result = 141 mojo::ReadMessageRaw(mojo::MessagePipeHandle(self->h1_), &self->buffer_, 142 nullptr, MOJO_READ_MESSAGE_FLAG_NONE); 143 assert(result == MOJO_RESULT_OK); 144 } 145 146 static void MessagePipe_EmptyRead(void* closure) { 147 CorePerftest* self = static_cast<CorePerftest*>(closure); 148 MojoMessageHandle message; 149 MojoResult result = MojoReadMessage(self->h0_, nullptr, &message); 150 ALLOW_UNUSED_LOCAL(result); 151 assert(result == MOJO_RESULT_SHOULD_WAIT); 152 } 153 154 protected: 155 #if !defined(WIN32) 156 void DoMessagePipeThreadedTest(unsigned num_writers, 157 unsigned num_readers, 158 uint32_t num_bytes) { 159 static const int64_t kPerftestTimeMicroseconds = 3 * 1000000; 160 161 assert(num_writers > 0); 162 assert(num_readers > 0); 163 164 MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); 165 ALLOW_UNUSED_LOCAL(result); 166 assert(result == MOJO_RESULT_OK); 167 168 std::vector<MessagePipeWriterThread*> writers; 169 for (unsigned i = 0; i < num_writers; i++) 170 writers.push_back(new MessagePipeWriterThread(h0_, num_bytes)); 171 172 std::vector<MessagePipeReaderThread*> readers; 173 for (unsigned i = 0; i < num_readers; i++) 174 readers.push_back(new MessagePipeReaderThread(h1_)); 175 176 // Start time here, just before we fire off the threads. 177 const MojoTimeTicks start_time = MojoGetTimeTicksNow(); 178 179 // Interleave the starts. 180 for (unsigned i = 0; i < num_writers || i < num_readers; i++) { 181 if (i < num_writers) 182 writers[i]->Start(); 183 if (i < num_readers) 184 readers[i]->Start(); 185 } 186 187 Sleep(kPerftestTimeMicroseconds); 188 189 // Close both handles to make writers and readers stop immediately. 190 result = MojoClose(h0_); 191 assert(result == MOJO_RESULT_OK); 192 result = MojoClose(h1_); 193 assert(result == MOJO_RESULT_OK); 194 195 // Join everything. 196 for (unsigned i = 0; i < num_writers; i++) 197 writers[i]->Join(); 198 for (unsigned i = 0; i < num_readers; i++) 199 readers[i]->Join(); 200 201 // Stop time here. 202 MojoTimeTicks end_time = MojoGetTimeTicksNow(); 203 204 // Add up write and read counts, and destroy the threads. 205 int64_t num_writes = 0; 206 for (unsigned i = 0; i < num_writers; i++) { 207 num_writes += writers[i]->num_writes(); 208 delete writers[i]; 209 } 210 writers.clear(); 211 int64_t num_reads = 0; 212 for (unsigned i = 0; i < num_readers; i++) { 213 num_reads += readers[i]->num_reads(); 214 delete readers[i]; 215 } 216 readers.clear(); 217 218 char sub_test_name[200]; 219 sprintf(sub_test_name, "%uw_%ur_%ubytes", num_writers, num_readers, 220 static_cast<unsigned>(num_bytes)); 221 mojo::test::LogPerfResult( 222 "MessagePipe_Threaded_Writes", sub_test_name, 223 1000000.0 * static_cast<double>(num_writes) / (end_time - start_time), 224 "writes/second"); 225 mojo::test::LogPerfResult( 226 "MessagePipe_Threaded_Reads", sub_test_name, 227 1000000.0 * static_cast<double>(num_reads) / (end_time - start_time), 228 "reads/second"); 229 } 230 #endif // !defined(WIN32) 231 232 MojoHandle h0_; 233 MojoHandle h1_; 234 235 std::vector<uint8_t> buffer_; 236 237 private: 238 #if !defined(WIN32) 239 void Sleep(int64_t microseconds) { 240 struct timespec req = { 241 static_cast<time_t>(microseconds / 1000000), // Seconds. 242 static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds. 243 }; 244 int rv = nanosleep(&req, nullptr); 245 ALLOW_UNUSED_LOCAL(rv); 246 assert(rv == 0); 247 } 248 #endif // !defined(WIN32) 249 250 DISALLOW_COPY_AND_ASSIGN(CorePerftest); 251 }; 252 253 // A no-op test so we can compare performance. 254 TEST_F(CorePerftest, NoOp) { 255 mojo::test::IterateAndReportPerf("Iterate_NoOp", nullptr, &CorePerftest::NoOp, 256 this); 257 } 258 259 TEST_F(CorePerftest, MessagePipe_CreateAndClose) { 260 mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", nullptr, 261 &CorePerftest::MessagePipe_CreateAndClose, 262 this); 263 } 264 265 TEST_F(CorePerftest, MessagePipe_WriteAndRead) { 266 MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_); 267 ALLOW_UNUSED_LOCAL(result); 268 assert(result == MOJO_RESULT_OK); 269 buffer_.resize(10); 270 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10bytes", 271 &CorePerftest::MessagePipe_WriteAndRead, 272 this); 273 buffer_.resize(100); 274 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "100bytes", 275 &CorePerftest::MessagePipe_WriteAndRead, 276 this); 277 buffer_.resize(1000); 278 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "1000bytes", 279 &CorePerftest::MessagePipe_WriteAndRead, 280 this); 281 buffer_.resize(10000); 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(nullptr, &h0_, &h1_); 293 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 326