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