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.
440 lines
15 KiB
440 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;
|
|
}
|