| /* |
| * 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; |
| } |