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 25namespace { 26 27#if !defined(WIN32) 28class 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 virtual ~MessagePipeWriterThread() {} 33 34 virtual void Run() MOJO_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 66class MessagePipeReaderThread : public mojo::Thread { 67 public: 68 explicit MessagePipeReaderThread(MojoHandle handle) 69 : handle_(handle), num_reads_(0) {} 70 virtual ~MessagePipeReaderThread() {} 71 72 virtual void Run() MOJO_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( 86 handle_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); 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 112class CorePerftest : public testing::Test { 113 public: 114 CorePerftest() : buffer_(NULL), num_bytes_(0) {} 115 virtual ~CorePerftest() {} 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 MOJO_ALLOW_UNUSED; 122 result = MojoCreateMessagePipe(NULL, &self->h0_, &self->h1_); 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 MOJO_ALLOW_UNUSED; 133 result = MojoWriteMessage(self->h0_, 134 self->buffer_, 135 self->num_bytes_, 136 NULL, 137 0, 138 MOJO_WRITE_MESSAGE_FLAG_NONE); 139 assert(result == MOJO_RESULT_OK); 140 uint32_t read_bytes = self->num_bytes_; 141 result = MojoReadMessage(self->h1_, 142 self->buffer_, 143 &read_bytes, 144 NULL, 145 NULL, 146 MOJO_READ_MESSAGE_FLAG_NONE); 147 assert(result == MOJO_RESULT_OK); 148 } 149 150 static void MessagePipe_EmptyRead(void* closure) { 151 CorePerftest* self = static_cast<CorePerftest*>(closure); 152 MojoResult result MOJO_ALLOW_UNUSED; 153 result = MojoReadMessage( 154 self->h0_, NULL, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); 155 assert(result == MOJO_RESULT_SHOULD_WAIT); 156 } 157 158 protected: 159#if !defined(WIN32) 160 void DoMessagePipeThreadedTest(unsigned num_writers, 161 unsigned num_readers, 162 uint32_t num_bytes) { 163 static const int64_t kPerftestTimeMicroseconds = 3 * 1000000; 164 165 assert(num_writers > 0); 166 assert(num_readers > 0); 167 168 MojoResult result MOJO_ALLOW_UNUSED; 169 result = MojoCreateMessagePipe(NULL, &h0_, &h1_); 170 assert(result == MOJO_RESULT_OK); 171 172 std::vector<MessagePipeWriterThread*> writers; 173 for (unsigned i = 0; i < num_writers; i++) 174 writers.push_back(new MessagePipeWriterThread(h0_, num_bytes)); 175 176 std::vector<MessagePipeReaderThread*> readers; 177 for (unsigned i = 0; i < num_readers; i++) 178 readers.push_back(new MessagePipeReaderThread(h1_)); 179 180 // Start time here, just before we fire off the threads. 181 const MojoTimeTicks start_time = MojoGetTimeTicksNow(); 182 183 // Interleave the starts. 184 for (unsigned i = 0; i < num_writers || i < num_readers; i++) { 185 if (i < num_writers) 186 writers[i]->Start(); 187 if (i < num_readers) 188 readers[i]->Start(); 189 } 190 191 Sleep(kPerftestTimeMicroseconds); 192 193 // Close both handles to make writers and readers stop immediately. 194 result = MojoClose(h0_); 195 assert(result == MOJO_RESULT_OK); 196 result = MojoClose(h1_); 197 assert(result == MOJO_RESULT_OK); 198 199 // Join everything. 200 for (unsigned i = 0; i < num_writers; i++) 201 writers[i]->Join(); 202 for (unsigned i = 0; i < num_readers; i++) 203 readers[i]->Join(); 204 205 // Stop time here. 206 MojoTimeTicks end_time = MojoGetTimeTicksNow(); 207 208 // Add up write and read counts, and destroy the threads. 209 int64_t num_writes = 0; 210 for (unsigned i = 0; i < num_writers; i++) { 211 num_writes += writers[i]->num_writes(); 212 delete writers[i]; 213 } 214 writers.clear(); 215 int64_t num_reads = 0; 216 for (unsigned i = 0; i < num_readers; i++) { 217 num_reads += readers[i]->num_reads(); 218 delete readers[i]; 219 } 220 readers.clear(); 221 222 char test_name[200]; 223 sprintf(test_name, 224 "MessagePipe_Threaded_Writes_%uw_%ur_%ubytes", 225 num_writers, 226 num_readers, 227 static_cast<unsigned>(num_bytes)); 228 mojo::test::LogPerfResult( 229 test_name, 230 1000000.0 * static_cast<double>(num_writes) / (end_time - start_time), 231 "writes/second"); 232 sprintf(test_name, 233 "MessagePipe_Threaded_Reads_%uw_%ur_%ubytes", 234 num_writers, 235 num_readers, 236 static_cast<unsigned>(num_bytes)); 237 mojo::test::LogPerfResult( 238 test_name, 239 1000000.0 * static_cast<double>(num_reads) / (end_time - start_time), 240 "reads/second"); 241 } 242#endif // !defined(WIN32) 243 244 MojoHandle h0_; 245 MojoHandle h1_; 246 247 void* buffer_; 248 uint32_t num_bytes_; 249 250 private: 251#if !defined(WIN32) 252 void Sleep(int64_t microseconds) { 253 struct timespec req = { 254 static_cast<time_t>(microseconds / 1000000), // Seconds. 255 static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds. 256 }; 257 int rv MOJO_ALLOW_UNUSED; 258 rv = nanosleep(&req, NULL); 259 assert(rv == 0); 260 } 261#endif // !defined(WIN32) 262 263 MOJO_DISALLOW_COPY_AND_ASSIGN(CorePerftest); 264}; 265 266// A no-op test so we can compare performance. 267TEST_F(CorePerftest, NoOp) { 268 mojo::test::IterateAndReportPerf("NoOp", &CorePerftest::NoOp, this); 269} 270 271TEST_F(CorePerftest, MessagePipe_CreateAndClose) { 272 mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose", 273 &CorePerftest::MessagePipe_CreateAndClose, 274 this); 275} 276 277TEST_F(CorePerftest, MessagePipe_WriteAndRead) { 278 MojoResult result MOJO_ALLOW_UNUSED; 279 result = MojoCreateMessagePipe(NULL, &h0_, &h1_); 280 assert(result == MOJO_RESULT_OK); 281 char buffer[10000] = {0}; 282 buffer_ = buffer; 283 num_bytes_ = 10u; 284 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10bytes", 285 &CorePerftest::MessagePipe_WriteAndRead, 286 this); 287 num_bytes_ = 100u; 288 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_100bytes", 289 &CorePerftest::MessagePipe_WriteAndRead, 290 this); 291 num_bytes_ = 1000u; 292 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_1000bytes", 293 &CorePerftest::MessagePipe_WriteAndRead, 294 this); 295 num_bytes_ = 10000u; 296 mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10000bytes", 297 &CorePerftest::MessagePipe_WriteAndRead, 298 this); 299 result = MojoClose(h0_); 300 assert(result == MOJO_RESULT_OK); 301 result = MojoClose(h1_); 302 assert(result == MOJO_RESULT_OK); 303} 304 305TEST_F(CorePerftest, MessagePipe_EmptyRead) { 306 MojoResult result MOJO_ALLOW_UNUSED; 307 result = MojoCreateMessagePipe(NULL, &h0_, &h1_); 308 assert(result == MOJO_RESULT_OK); 309 mojo::test::IterateAndReportPerf( 310 "MessagePipe_EmptyRead", &CorePerftest::MessagePipe_EmptyRead, this); 311 result = MojoClose(h0_); 312 assert(result == MOJO_RESULT_OK); 313 result = MojoClose(h1_); 314 assert(result == MOJO_RESULT_OK); 315} 316 317#if !defined(WIN32) 318TEST_F(CorePerftest, MessagePipe_Threaded) { 319 DoMessagePipeThreadedTest(1u, 1u, 100u); 320 DoMessagePipeThreadedTest(2u, 2u, 100u); 321 DoMessagePipeThreadedTest(3u, 3u, 100u); 322 DoMessagePipeThreadedTest(10u, 10u, 100u); 323 DoMessagePipeThreadedTest(10u, 1u, 100u); 324 DoMessagePipeThreadedTest(1u, 10u, 100u); 325 326 // For comparison of overhead: 327 DoMessagePipeThreadedTest(1u, 1u, 10u); 328 // 100 was done above. 329 DoMessagePipeThreadedTest(1u, 1u, 1000u); 330 DoMessagePipeThreadedTest(1u, 1u, 10000u); 331 332 DoMessagePipeThreadedTest(3u, 3u, 10u); 333 // 100 was done above. 334 DoMessagePipeThreadedTest(3u, 3u, 1000u); 335 DoMessagePipeThreadedTest(3u, 3u, 10000u); 336} 337#endif // !defined(WIN32) 338 339} // namespace 340