Home | History | Annotate | Download | only in benchmarks
      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:
     68     virtual void TearDown() {
     69         delete mFmqInbox;
     70         delete mFmqOutbox;
     71     }
     72 
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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