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