1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18
19 #include <gtest/gtest.h>
20 #include <utils/StrongPointer.h>
21 #include <chrono>
22 #include <iostream>
23
24 #include <android/hardware/tests/msgq/1.0/IBenchmarkMsgQ.h>
25 #include <fmq/MessageQueue.h>
26
27 // libutils:
28 using android::OK;
29 using android::sp;
30 using android::status_t;
31
32 // generated
33 using android::hardware::tests::msgq::V1_0::IBenchmarkMsgQ;
34 using std::cerr;
35 using std::cout;
36 using std::endl;
37
38 // libhidl
39 using android::hardware::kSynchronizedReadWrite;
40 using android::hardware::MQDescriptorSync;
41 using android::hardware::MessageQueue;
42
43 /*
44 * All the benchmark cases will be performed on an FMQ of size kQueueSize.
45 */
46 static const int32_t kQueueSize = 1024 * 16;
47
48 /*
49 * The number of iterations for each experiment.
50 */
51 static const uint32_t kNumIterations = 1000;
52
53 /*
54 * The various packet sizes used are as follows.
55 */
56 enum PacketSizes {
57 kPacketSize64 = 64,
58 kPacketSize128 = 128,
59 kPacketSize256 = 256,
60 kPacketSize512 = 512,
61 kPacketSize1024 = 1024
62 };
63
64 class MQTestClient : public ::testing::Test {
65 protected:
TearDown()66 virtual void TearDown() {
67 delete mFmqInbox;
68 delete mFmqOutbox;
69 }
70
SetUp()71 virtual void SetUp() {
72 service = IBenchmarkMsgQ::getService();
73 ASSERT_NE(service, nullptr);
74 ASSERT_TRUE(service->isRemote());
75 /*
76 * Request service to configure the client inbox queue.
77 */
78 service->configureClientInboxSyncReadWrite([this](bool ret,
79 const MQDescriptorSync<uint8_t>& in) {
80 ASSERT_TRUE(ret);
81 mFmqInbox = new (std::nothrow) MessageQueue<uint8_t, kSynchronizedReadWrite>(in);
82 });
83
84 ASSERT_TRUE(mFmqInbox != nullptr);
85 ASSERT_TRUE(mFmqInbox->isValid());
86 /*
87 * Reqeust service to configure the client outbox queue.
88 */
89 service->configureClientOutboxSyncReadWrite([this](bool ret,
90 const MQDescriptorSync<uint8_t>& out) {
91 ASSERT_TRUE(ret);
92 mFmqOutbox = new (std::nothrow) MessageQueue<uint8_t,
93 kSynchronizedReadWrite>(out);
94 });
95
96 ASSERT_TRUE(mFmqOutbox != nullptr);
97 ASSERT_TRUE(mFmqOutbox->isValid());
98 }
99
100 sp<IBenchmarkMsgQ> service;
101 android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqInbox = nullptr;
102 android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqOutbox = nullptr;
103 };
104
105 /*
106 * Client writes a 64 byte packet into the outbox queue, service reads the
107 * same and
108 * writes the packet into the client's inbox queue. Client reads the packet. The
109 * average time taken for the cycle is measured.
110 */
TEST_F(MQTestClient,BenchMarkMeasurePingPongTransfer)111 TEST_F(MQTestClient, BenchMarkMeasurePingPongTransfer) {
112 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
113 ASSERT_TRUE(data != nullptr);
114 int64_t accumulatedTime = 0;
115 size_t numRoundTrips = 0;
116
117 /*
118 * This method requests the service to create a thread which reads
119 * from mFmqOutbox and writes into mFmqInbox.
120 */
121 service->benchmarkPingPong(kNumIterations);
122 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
123 std::chrono::high_resolution_clock::now();
124 while (numRoundTrips < kNumIterations) {
125 while (mFmqOutbox->write(data, kPacketSize64) == 0) {
126 }
127
128 while (mFmqInbox->read(data, kPacketSize64) == 0) {
129 }
130
131 numRoundTrips++;
132 }
133 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
134 std::chrono::high_resolution_clock::now();
135 accumulatedTime += static_cast<int64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
136 timeEnd - timeStart).count());
137 accumulatedTime /= kNumIterations;
138
139 cout << "Round trip time for " << kPacketSize64 << "bytes: " <<
140 accumulatedTime << "ns" << endl;
141 delete[] data;
142 }
143
144 /*
145 * Measure the average time taken to read 64 bytes from the queue.
146 */
TEST_F(MQTestClient,BenchMarkMeasureRead64Bytes)147 TEST_F(MQTestClient, BenchMarkMeasureRead64Bytes) {
148 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
149 ASSERT_TRUE(data != nullptr);
150
151 uint32_t numLoops = kQueueSize / kPacketSize64;
152 uint64_t accumulatedTime = 0;
153 for (uint32_t i = 0; i < kNumIterations; i++) {
154 bool ret = service->requestWrite(kQueueSize);
155 ASSERT_TRUE(ret);
156 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
157 std::chrono::high_resolution_clock::now();
158 /*
159 * The read() method returns true only if the the correct number of bytes
160 * were succesfully read from the queue.
161 */
162 for (uint32_t j = 0; j < numLoops; j++) {
163 ASSERT_TRUE(mFmqInbox->read(data, kPacketSize64));
164 }
165
166 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
167 std::chrono::high_resolution_clock::now();
168 accumulatedTime += (timeEnd - timeStart).count();
169 }
170
171 accumulatedTime /= (numLoops * kNumIterations);
172 cout << "Average time to read" << kPacketSize64
173 << "bytes: " << accumulatedTime << "ns" << endl;
174 delete[] data;
175 }
176
177 /*
178 * Measure the average time taken to read 128 bytes.
179 */
TEST_F(MQTestClient,BenchMarkMeasureRead128Bytes)180 TEST_F(MQTestClient, BenchMarkMeasureRead128Bytes) {
181 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128];
182 ASSERT_TRUE(data != nullptr);
183
184 uint32_t numLoops = kQueueSize / kPacketSize128;
185 uint64_t accumulatedTime = 0;
186
187 for (uint32_t i = 0; i < kNumIterations; i++) {
188 bool ret = service->requestWrite(kQueueSize);
189 ASSERT_TRUE(ret);
190 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
191 std::chrono::high_resolution_clock::now();
192
193 /*
194 * The read() method returns true only if the the correct number of bytes
195 * were succesfully read from the queue.
196 */
197 for (uint32_t j = 0; j < numLoops; j++) {
198 ASSERT_TRUE(mFmqInbox->read(data, kPacketSize128));
199 }
200 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
201 std::chrono::high_resolution_clock::now();
202 accumulatedTime += (timeEnd - timeStart).count();
203 }
204
205 accumulatedTime /= (numLoops * kNumIterations);
206 cout << "Average time to read" << kPacketSize128
207 << "bytes: " << accumulatedTime << "ns" << endl;
208 delete[] data;
209 }
210
211 /*
212 * Measure the average time taken to read 256 bytes from the queue.
213 */
TEST_F(MQTestClient,BenchMarkMeasureRead256Bytes)214 TEST_F(MQTestClient, BenchMarkMeasureRead256Bytes) {
215 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256];
216 ASSERT_TRUE(data != nullptr);
217 uint32_t numLoops = kQueueSize / kPacketSize256;
218 uint64_t accumulatedTime = 0;
219
220 for (uint32_t i = 0; i < kNumIterations; i++) {
221 bool ret = service->requestWrite(kQueueSize);
222 ASSERT_TRUE(ret);
223 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
224 std::chrono::high_resolution_clock::now();
225 /*
226 * The read() method returns true only if the the correct number of bytes
227 * were succesfully read from the queue.
228 */
229 for (uint32_t j = 0; j < numLoops; j++) {
230 ASSERT_TRUE(mFmqInbox->read(data, kPacketSize256));
231 }
232
233 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
234 std::chrono::high_resolution_clock::now();
235 accumulatedTime += (timeEnd - timeStart).count();
236 }
237
238 accumulatedTime /= (numLoops * kNumIterations);
239 cout << "Average time to read" << kPacketSize256
240 << "bytes: " << accumulatedTime << "ns" << endl;
241 delete[] data;
242 }
243
244 /*
245 * Measure the average time taken to read 512 bytes from the queue.
246 */
TEST_F(MQTestClient,BenchMarkMeasureRead512Bytes)247 TEST_F(MQTestClient, BenchMarkMeasureRead512Bytes) {
248 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512];
249 ASSERT_TRUE(data != nullptr);
250 uint32_t numLoops = kQueueSize / kPacketSize512;
251 uint64_t accumulatedTime = 0;
252 for (uint32_t i = 0; i < kNumIterations; i++) {
253 bool ret = service->requestWrite(kQueueSize);
254 ASSERT_TRUE(ret);
255 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
256 std::chrono::high_resolution_clock::now();
257 /*
258 * The read() method returns true only if the the correct number of bytes
259 * were succesfully read from the queue.
260 */
261 for (uint32_t j = 0; j < numLoops; j++) {
262 ASSERT_TRUE(mFmqInbox->read(data, kPacketSize512));
263 }
264 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
265 std::chrono::high_resolution_clock::now();
266 accumulatedTime += (timeEnd - timeStart).count();
267 }
268
269 accumulatedTime /= (numLoops * kNumIterations);
270 cout << "Average time to read" << kPacketSize512
271 << "bytes: " << accumulatedTime << "ns" << endl;
272 delete[] data;
273 }
274
275 /*
276 * Measure the average time taken to write 64 bytes into the queue.
277 */
TEST_F(MQTestClient,BenchMarkMeasureWrite64Bytes)278 TEST_F(MQTestClient, BenchMarkMeasureWrite64Bytes) {
279 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
280 ASSERT_TRUE(data != nullptr);
281 uint32_t numLoops = kQueueSize / kPacketSize64;
282 uint64_t accumulatedTime = 0;
283
284 for (uint32_t i = 0; i < kNumIterations; i++) {
285 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
286 std::chrono::high_resolution_clock::now();
287 /*
288 * Write until the queue is full and request service to empty the queue.
289 */
290 for (uint32_t j = 0; j < numLoops; j++) {
291 bool result = mFmqOutbox->write(data, kPacketSize64);
292 ASSERT_TRUE(result);
293 }
294
295 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
296 std::chrono::high_resolution_clock::now();
297 accumulatedTime += (timeEnd - timeStart).count();
298
299 bool ret = service->requestRead(kQueueSize);
300 ASSERT_TRUE(ret);
301 }
302
303 accumulatedTime /= (numLoops * kNumIterations);
304 cout << "Average time to write " << kPacketSize64
305 << "bytes: " << accumulatedTime << "ns" << endl;
306 delete[] data;
307 }
308
309 /*
310 * Measure the average time taken to write 128 bytes into the queue.
311 */
TEST_F(MQTestClient,BenchMarkMeasureWrite128Bytes)312 TEST_F(MQTestClient, BenchMarkMeasureWrite128Bytes) {
313 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128];
314 ASSERT_TRUE(data != nullptr);
315 uint32_t numLoops = kQueueSize / kPacketSize128;
316 uint64_t accumulatedTime = 0;
317
318 for (uint32_t i = 0; i < kNumIterations; i++) {
319 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
320 std::chrono::high_resolution_clock::now();
321 /*
322 * Write until the queue is full and request service to empty the queue.
323 */
324 for (uint32_t j = 0; j < numLoops; j++) {
325 ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize128));
326 }
327
328 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
329 std::chrono::high_resolution_clock::now();
330 accumulatedTime += (timeEnd - timeStart).count();
331
332 bool ret = service->requestRead(kQueueSize);
333 ASSERT_TRUE(ret);
334 }
335
336 accumulatedTime /= (numLoops * kNumIterations);
337 cout << "Average time to write " << kPacketSize128
338 << "bytes: " << accumulatedTime << "ns" << endl;
339 delete[] data;
340 }
341
342 /*
343 * Measure the average time taken to write 256 bytes into the queue.
344 */
TEST_F(MQTestClient,BenchMarkMeasureWrite256Bytes)345 TEST_F(MQTestClient, BenchMarkMeasureWrite256Bytes) {
346 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256];
347 ASSERT_TRUE(data != nullptr);
348 uint32_t numLoops = kQueueSize / kPacketSize256;
349 uint64_t accumulatedTime = 0;
350
351 for (uint32_t i = 0; i < kNumIterations; i++) {
352 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
353 std::chrono::high_resolution_clock::now();
354 /*
355 * Write until the queue is full and request service to empty the queue.
356 */
357 for (uint32_t j = 0; j < numLoops; j++) {
358 ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize256));
359 }
360 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
361 std::chrono::high_resolution_clock::now();
362 accumulatedTime += (timeEnd - timeStart).count();
363
364 bool ret = service->requestRead(kQueueSize);
365 ASSERT_TRUE(ret);
366 }
367
368 accumulatedTime /= (numLoops * kNumIterations);
369 cout << "Average time to write " << kPacketSize256
370 << "bytes: " << accumulatedTime << "ns" << endl;
371 delete[] data;
372 }
373
374 /*
375 * Measure the average time taken to write 512 bytes into the queue.
376 */
TEST_F(MQTestClient,BenchMarkMeasureWrite512Bytes)377 TEST_F(MQTestClient, BenchMarkMeasureWrite512Bytes) {
378 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512];
379 ASSERT_TRUE(data != nullptr);
380 uint32_t numLoops = kQueueSize / kPacketSize512;
381 uint64_t accumulatedTime = 0;
382
383 for (uint32_t i = 0; i < kNumIterations; i++) {
384 std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
385 std::chrono::high_resolution_clock::now();
386
387 /*
388 * Write until the queue is full and request service to empty the queue.
389 * The write() method returns true only if the specified number of bytes
390 * were succesfully written.
391 */
392 for (uint32_t j = 0; j < numLoops; j++) {
393 ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize512));
394 }
395
396 std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
397 std::chrono::high_resolution_clock::now();
398 accumulatedTime += (timeEnd - timeStart).count();
399
400 bool ret = service->requestRead(kQueueSize);
401 ASSERT_TRUE(ret);
402 }
403
404 accumulatedTime /= (numLoops * kNumIterations);
405 cout << "Average time to write " << kPacketSize512
406 << "bytes: " << accumulatedTime << "ns" << endl;
407 delete[] data;
408 }
409
410 /*
411 * Service continuously writes a packet of 64 bytes into the client's inbox
412 * queue
413 * of size 16K. Client keeps reading from the inbox queue. The average write to
414 * read delay is calculated.
415 */
TEST_F(MQTestClient,BenchMarkMeasureServiceWriteClientRead)416 TEST_F(MQTestClient, BenchMarkMeasureServiceWriteClientRead) {
417 uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
418 ASSERT_TRUE(data != nullptr);
419 /*
420 * This method causes the service to create a thread which writes
421 * into the mFmqInbox queue kNumIterations packets.
422 */
423 service->benchmarkServiceWriteClientRead(kNumIterations);
424 android::hardware::hidl_vec<int64_t> clientRcvTimeArray;
425 clientRcvTimeArray.resize(kNumIterations);
426 for (uint32_t i = 0; i < kNumIterations; i++) {
427 do {
428 clientRcvTimeArray[i] =
429 std::chrono::high_resolution_clock::now().time_since_epoch().count();
430 } while (mFmqInbox->read(data, kPacketSize64) == 0);
431 }
432 service->sendTimeData(clientRcvTimeArray);
433 delete[] data;
434 }
435