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 23namespace { 24 25#if !defined(WIN32) 26class MessagePipeWriterThread : public base::SimpleThread { 27 public: 28 MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes) 29 : SimpleThread("MessagePipeWriterThread"), 30 handle_(handle), 31 num_bytes_(num_bytes), 32 num_writes_(0) {} 33 ~MessagePipeWriterThread() override {} 34 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. 57 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 67class MessagePipeReaderThread : public base::SimpleThread { 68 public: 69 explicit MessagePipeReaderThread(MojoHandle handle) 70 : SimpleThread("MessagePipeReaderThread"), 71 handle_(handle), 72 num_reads_(0) {} 73 ~MessagePipeReaderThread() override {} 74 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. 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 115class CorePerftest : public testing::Test { 116 public: 117 CorePerftest() : buffer_(nullptr), num_bytes_(0) {} 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 = 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 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) 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) 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. 256TEST_F(CorePerftest, NoOp) { 257 mojo::test::IterateAndReportPerf("Iterate_NoOp", nullptr, &CorePerftest::NoOp, 258 this); 259} 260 261TEST_F(CorePerftest, MessagePipe_CreateAndClose) { 262 mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", nullptr, 263 &CorePerftest::MessagePipe_CreateAndClose, 264 this); 265} 266 267TEST_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 295TEST_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) 308TEST_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