You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
15 KiB
439 lines
15 KiB
/* |
|
* 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; |
|
}
|
|
|