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