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