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