/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <android-base/logging.h> #include <gtest/gtest.h> #include <utils/StrongPointer.h> #include <chrono> #include <iostream> #include <android/hardware/tests/msgq/1.0/IBenchmarkMsgQ.h> #include <fmq/MessageQueue.h> #include <hidl/ServiceManagement.h> // libutils: using android::OK; using android::sp; using android::status_t; // generated using android::hardware::tests::msgq::V1_0::IBenchmarkMsgQ; using std::cerr; using std::cout; using std::endl; // libhidl using android::hardware::kSynchronizedReadWrite; using android::hardware::MQDescriptorSync; using android::hardware::MessageQueue; using android::hardware::details::waitForHwService; /* * All the benchmark cases will be performed on an FMQ of size kQueueSize. */ static const int32_t kQueueSize = 1024 * 16; /* * The number of iterations for each experiment. */ static const uint32_t kNumIterations = 1000; /* * The various packet sizes used are as follows. */ enum PacketSizes { kPacketSize64 = 64, kPacketSize128 = 128, kPacketSize256 = 256, kPacketSize512 = 512, kPacketSize1024 = 1024 }; class MQTestClient : public ::testing::Test { protected: virtual void TearDown() { delete mFmqInbox; delete mFmqOutbox; } virtual void SetUp() { // waitForHwService is required because IBenchmarkMsgQ is not in manifest.xml. // "Real" HALs shouldn't be doing this. waitForHwService(IBenchmarkMsgQ::descriptor, "default"); service = IBenchmarkMsgQ::getService(); ASSERT_NE(service, nullptr); ASSERT_TRUE(service->isRemote()); /* * Request service to configure the client inbox queue. */ service->configureClientInboxSyncReadWrite([this](bool ret, const MQDescriptorSync<uint8_t>& in) { ASSERT_TRUE(ret); mFmqInbox = new (std::nothrow) MessageQueue<uint8_t, kSynchronizedReadWrite>(in); }); ASSERT_TRUE(mFmqInbox != nullptr); ASSERT_TRUE(mFmqInbox->isValid()); /* * Reqeust service to configure the client outbox queue. */ service->configureClientOutboxSyncReadWrite([this](bool ret, const MQDescriptorSync<uint8_t>& out) { ASSERT_TRUE(ret); mFmqOutbox = new (std::nothrow) MessageQueue<uint8_t, kSynchronizedReadWrite>(out); }); ASSERT_TRUE(mFmqOutbox != nullptr); ASSERT_TRUE(mFmqOutbox->isValid()); } sp<IBenchmarkMsgQ> service; android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqInbox = nullptr; android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqOutbox = nullptr; }; /* * Client writes a 64 byte packet into the outbox queue, service reads the * same and * writes the packet into the client's inbox queue. Client reads the packet. The * average time taken for the cycle is measured. */ TEST_F(MQTestClient, BenchMarkMeasurePingPongTransfer) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64]; ASSERT_TRUE(data != nullptr); int64_t accumulatedTime = 0; size_t numRoundTrips = 0; /* * This method requests the service to create a thread which reads * from mFmqOutbox and writes into mFmqInbox. */ service->benchmarkPingPong(kNumIterations); std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); while (numRoundTrips < kNumIterations) { while (mFmqOutbox->write(data, kPacketSize64) == 0) { } while (mFmqInbox->read(data, kPacketSize64) == 0) { } numRoundTrips++; } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += static_cast<int64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>( timeEnd - timeStart).count()); accumulatedTime /= kNumIterations; cout << "Round trip time for " << kPacketSize64 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to read 64 bytes from the queue. */ TEST_F(MQTestClient, BenchMarkMeasureRead64Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize64; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { bool ret = service->requestWrite(kQueueSize); ASSERT_TRUE(ret); std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * The read() method returns true only if the the correct number of bytes * were succesfully read from the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqInbox->read(data, kPacketSize64)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to read" << kPacketSize64 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to read 128 bytes. */ TEST_F(MQTestClient, BenchMarkMeasureRead128Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize128; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { bool ret = service->requestWrite(kQueueSize); ASSERT_TRUE(ret); std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * The read() method returns true only if the the correct number of bytes * were succesfully read from the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqInbox->read(data, kPacketSize128)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to read" << kPacketSize128 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to read 256 bytes from the queue. */ TEST_F(MQTestClient, BenchMarkMeasureRead256Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize256; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { bool ret = service->requestWrite(kQueueSize); ASSERT_TRUE(ret); std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * The read() method returns true only if the the correct number of bytes * were succesfully read from the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqInbox->read(data, kPacketSize256)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to read" << kPacketSize256 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to read 512 bytes from the queue. */ TEST_F(MQTestClient, BenchMarkMeasureRead512Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize512; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { bool ret = service->requestWrite(kQueueSize); ASSERT_TRUE(ret); std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * The read() method returns true only if the the correct number of bytes * were succesfully read from the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqInbox->read(data, kPacketSize512)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to read" << kPacketSize512 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to write 64 bytes into the queue. */ TEST_F(MQTestClient, BenchMarkMeasureWrite64Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize64; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * Write until the queue is full and request service to empty the queue. */ for (uint32_t j = 0; j < numLoops; j++) { bool result = mFmqOutbox->write(data, kPacketSize64); ASSERT_TRUE(result); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); bool ret = service->requestRead(kQueueSize); ASSERT_TRUE(ret); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to write " << kPacketSize64 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to write 128 bytes into the queue. */ TEST_F(MQTestClient, BenchMarkMeasureWrite128Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize128; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * Write until the queue is full and request service to empty the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize128)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); bool ret = service->requestRead(kQueueSize); ASSERT_TRUE(ret); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to write " << kPacketSize128 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to write 256 bytes into the queue. */ TEST_F(MQTestClient, BenchMarkMeasureWrite256Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize256; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * Write until the queue is full and request service to empty the queue. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize256)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); bool ret = service->requestRead(kQueueSize); ASSERT_TRUE(ret); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to write " << kPacketSize256 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Measure the average time taken to write 512 bytes into the queue. */ TEST_F(MQTestClient, BenchMarkMeasureWrite512Bytes) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512]; ASSERT_TRUE(data != nullptr); uint32_t numLoops = kQueueSize / kPacketSize512; uint64_t accumulatedTime = 0; for (uint32_t i = 0; i < kNumIterations; i++) { std::chrono::time_point<std::chrono::high_resolution_clock> timeStart = std::chrono::high_resolution_clock::now(); /* * Write until the queue is full and request service to empty the queue. * The write() method returns true only if the specified number of bytes * were succesfully written. */ for (uint32_t j = 0; j < numLoops; j++) { ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize512)); } std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd = std::chrono::high_resolution_clock::now(); accumulatedTime += (timeEnd - timeStart).count(); bool ret = service->requestRead(kQueueSize); ASSERT_TRUE(ret); } accumulatedTime /= (numLoops * kNumIterations); cout << "Average time to write " << kPacketSize512 << "bytes: " << accumulatedTime << "ns" << endl; delete[] data; } /* * Service continuously writes a packet of 64 bytes into the client's inbox * queue * of size 16K. Client keeps reading from the inbox queue. The average write to * read delay is calculated. */ TEST_F(MQTestClient, BenchMarkMeasureServiceWriteClientRead) { uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64]; ASSERT_TRUE(data != nullptr); /* * This method causes the service to create a thread which writes * into the mFmqInbox queue kNumIterations packets. */ service->benchmarkServiceWriteClientRead(kNumIterations); android::hardware::hidl_vec<int64_t> clientRcvTimeArray; clientRcvTimeArray.resize(kNumIterations); for (uint32_t i = 0; i < kNumIterations; i++) { do { clientRcvTimeArray[i] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); } while (mFmqInbox->read(data, kPacketSize64) == 0); } service->sendTimeData(clientRcvTimeArray); delete[] data; }