blob: 6abbe3b9532b50cf55cb4817c10105530cb9701e [file] [log] [blame]
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -08001/*
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
Hridya Valsarajuef924bb2017-03-03 14:08:56 -080024#include <android/hardware/tests/msgq/1.0/IBenchmarkMsgQ.h>
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080025#include <fmq/MessageQueue.h>
Yifan Honge7262012017-07-31 14:11:56 -070026#include <hidl/ServiceManagement.h>
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080027
28// libutils:
29using android::OK;
30using android::sp;
31using android::status_t;
32
33// generated
Hridya Valsarajuef924bb2017-03-03 14:08:56 -080034using android::hardware::tests::msgq::V1_0::IBenchmarkMsgQ;
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080035using std::cerr;
36using std::cout;
37using std::endl;
38
39// libhidl
40using android::hardware::kSynchronizedReadWrite;
41using android::hardware::MQDescriptorSync;
42using android::hardware::MessageQueue;
Yifan Honge7262012017-07-31 14:11:56 -070043using android::hardware::details::waitForHwService;
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080044
45/*
46 * All the benchmark cases will be performed on an FMQ of size kQueueSize.
47 */
48static const int32_t kQueueSize = 1024 * 16;
49
50/*
51 * The number of iterations for each experiment.
52 */
53static const uint32_t kNumIterations = 1000;
54
55/*
56 * The various packet sizes used are as follows.
57 */
58enum PacketSizes {
59 kPacketSize64 = 64,
60 kPacketSize128 = 128,
61 kPacketSize256 = 256,
62 kPacketSize512 = 512,
63 kPacketSize1024 = 1024
64};
65
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080066class MQTestClient : public ::testing::Test {
67protected:
68 virtual void TearDown() {
69 delete mFmqInbox;
70 delete mFmqOutbox;
71 }
72
73 virtual void SetUp() {
Yifan Honge7262012017-07-31 14:11:56 -070074 // waitForHwService is required because IBenchmarkMsgQ is not in manifest.xml.
75 // "Real" HALs shouldn't be doing this.
76 waitForHwService(IBenchmarkMsgQ::descriptor, "default");
Hridya Valsaraju27493572017-03-07 20:17:54 -080077 service = IBenchmarkMsgQ::getService();
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080078 ASSERT_NE(service, nullptr);
Hridya Valsaraju27493572017-03-07 20:17:54 -080079 ASSERT_TRUE(service->isRemote());
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080080 /*
81 * Request service to configure the client inbox queue.
82 */
Hridya Valsaraju7eff3422016-12-27 11:54:13 -080083 service->configureClientInboxSyncReadWrite([this](bool ret,
84 const MQDescriptorSync<uint8_t>& in) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080085 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 */
Hridya Valsaraju7eff3422016-12-27 11:54:13 -080094 service->configureClientOutboxSyncReadWrite([this](bool ret,
95 const MQDescriptorSync<uint8_t>& out) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080096 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 */
116TEST_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 */
152TEST_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 */
185TEST_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 */
219TEST_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 */
252TEST_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 */
283TEST_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 */
317TEST_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 */
350TEST_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 */
382TEST_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 */
421TEST_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}