1 #include <errno.h> 2 #include <fcntl.h> 3 #include <time.h> 4 #include <unistd.h> 5 6 #include <chrono> 7 #include <iomanip> 8 #include <iostream> 9 #include <vector> 10 11 #include <pdx/rpc/argument_encoder.h> 12 #include <pdx/rpc/message_buffer.h> 13 #include <pdx/rpc/payload.h> 14 #include <pdx/utility.h> 15 16 using namespace android::pdx::rpc; 17 using namespace android::pdx; 18 using std::placeholders::_1; 19 using std::placeholders::_2; 20 using std::placeholders::_3; 21 using std::placeholders::_4; 22 using std::placeholders::_5; 23 using std::placeholders::_6; 24 25 namespace { 26 27 constexpr size_t kMaxStaticBufferSize = 20480; 28 29 // Provide numpunct facet that formats numbers with ',' as thousands separators. 30 class CommaNumPunct : public std::numpunct<char> { 31 protected: 32 char do_thousands_sep() const override { return ','; } 33 std::string do_grouping() const override { return "\03"; } 34 }; 35 36 class TestPayload : public MessagePayload<SendBuffer>, 37 public MessageWriter, 38 public MessageReader, 39 public NoOpResourceMapper { 40 public: 41 // MessageWriter 42 void* GetNextWriteBufferSection(size_t size) override { 43 const size_t section_offset = Size(); 44 Extend(size); 45 return Data() + section_offset; 46 } 47 48 OutputResourceMapper* GetOutputResourceMapper() override { return this; } 49 50 // MessageReader 51 BufferSection GetNextReadBufferSection() override { 52 return {&*ConstCursor(), &*ConstEnd()}; 53 } 54 55 void ConsumeReadBufferSectionData(const void* new_start) override { 56 std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor())); 57 } 58 59 InputResourceMapper* GetInputResourceMapper() override { return this; } 60 }; 61 62 class StaticBuffer : public MessageWriter, 63 public MessageReader, 64 public NoOpResourceMapper { 65 public: 66 void Clear() { 67 read_ptr_ = buffer_; 68 write_ptr_ = 0; 69 } 70 void Rewind() { read_ptr_ = buffer_; } 71 72 // MessageWriter 73 void* GetNextWriteBufferSection(size_t size) override { 74 void* ptr = buffer_ + write_ptr_; 75 write_ptr_ += size; 76 return ptr; 77 } 78 79 OutputResourceMapper* GetOutputResourceMapper() override { return this; } 80 81 // MessageReader 82 BufferSection GetNextReadBufferSection() override { 83 return {read_ptr_, std::end(buffer_)}; 84 } 85 86 void ConsumeReadBufferSectionData(const void* new_start) override { 87 read_ptr_ = static_cast<const uint8_t*>(new_start); 88 } 89 90 InputResourceMapper* GetInputResourceMapper() override { return this; } 91 92 private: 93 uint8_t buffer_[kMaxStaticBufferSize]; 94 const uint8_t* read_ptr_{buffer_}; 95 size_t write_ptr_{0}; 96 }; 97 98 // Simple callback function to clear/reset the input/output buffers for 99 // serialization. Using raw function pointer here instead of std::function to 100 // minimize the overhead of invocation in the tight test loop over millions of 101 // iterations. 102 using ResetFunc = void(void*); 103 104 // Serialization test function signature, used by the TestRunner. 105 using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer, 106 size_t iterations, 107 ResetFunc* write_reset, 108 void* reset_data); 109 110 // Deserialization test function signature, used by the TestRunner. 111 using DeserializeTestSignature = std::chrono::nanoseconds( 112 MessageReader* reader, MessageWriter* writer, size_t iterations, 113 ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data); 114 115 // Generic serialization test runner method. Takes the |value| of type T and 116 // serializes it into the output buffer represented by |writer|. 117 template <typename T> 118 std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer, 119 size_t iterations, 120 ResetFunc* write_reset, 121 void* reset_data, const T& value) { 122 auto start = std::chrono::high_resolution_clock::now(); 123 for (size_t i = 0; i < iterations; i++) { 124 write_reset(reset_data); 125 Serialize(value, writer); 126 } 127 auto stop = std::chrono::high_resolution_clock::now(); 128 return stop - start; 129 } 130 131 // Generic deserialization test runner method. Takes the |value| of type T and 132 // temporarily serializes it into the output buffer, then repeatedly 133 // deserializes the data back from that buffer. 134 template <typename T> 135 std::chrono::nanoseconds DeserializeTestRunner( 136 MessageReader* reader, MessageWriter* writer, size_t iterations, 137 ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, 138 const T& value) { 139 write_reset(reset_data); 140 Serialize(value, writer); 141 T output_data; 142 auto start = std::chrono::high_resolution_clock::now(); 143 for (size_t i = 0; i < iterations; i++) { 144 read_reset(reset_data); 145 Deserialize(&output_data, reader); 146 } 147 auto stop = std::chrono::high_resolution_clock::now(); 148 if (output_data != value) 149 return start - stop; // Return negative value to indicate error. 150 return stop - start; 151 } 152 153 // Special version of SerializeTestRunner that doesn't perform any serialization 154 // but does all the same setup steps and moves data of size |data_size| into 155 // the output buffer. Useful to determine the baseline to calculate time used 156 // just for serialization layer. 157 std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer, 158 size_t iterations, 159 ResetFunc* write_reset, 160 void* reset_data, size_t data_size) { 161 std::vector<uint8_t> dummy_data(data_size); 162 auto start = std::chrono::high_resolution_clock::now(); 163 for (size_t i = 0; i < iterations; i++) { 164 write_reset(reset_data); 165 memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), 166 dummy_data.data(), dummy_data.size()); 167 } 168 auto stop = std::chrono::high_resolution_clock::now(); 169 return stop - start; 170 } 171 172 // Special version of DeserializeTestRunner that doesn't perform any 173 // deserialization but invokes Rewind on the input buffer repeatedly. 174 // Useful to determine the baseline to calculate time used just for 175 // deserialization layer. 176 std::chrono::nanoseconds DeserializeBaseTest( 177 MessageReader* reader, MessageWriter* writer, size_t iterations, 178 ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, 179 size_t data_size) { 180 std::vector<uint8_t> dummy_data(data_size); 181 write_reset(reset_data); 182 memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), 183 dummy_data.data(), dummy_data.size()); 184 auto start = std::chrono::high_resolution_clock::now(); 185 for (size_t i = 0; i < iterations; i++) { 186 read_reset(reset_data); 187 auto section = reader->GetNextReadBufferSection(); 188 memcpy(dummy_data.data(), section.first, dummy_data.size()); 189 reader->ConsumeReadBufferSectionData( 190 AdvancePointer(section.first, dummy_data.size())); 191 } 192 auto stop = std::chrono::high_resolution_clock::now(); 193 return stop - start; 194 } 195 196 // The main class that accumulates individual tests to be executed. 197 class TestRunner { 198 public: 199 struct BufferInfo { 200 BufferInfo(const std::string& buffer_name, MessageReader* reader, 201 MessageWriter* writer, ResetFunc* read_reset_func, 202 ResetFunc* write_reset_func, void* reset_data) 203 : name{buffer_name}, 204 reader{reader}, 205 writer{writer}, 206 read_reset_func{read_reset_func}, 207 write_reset_func{write_reset_func}, 208 reset_data{reset_data} {} 209 std::string name; 210 MessageReader* reader; 211 MessageWriter* writer; 212 ResetFunc* read_reset_func; 213 ResetFunc* write_reset_func; 214 void* reset_data; 215 }; 216 217 void AddTestFunc(const std::string& name, 218 std::function<SerializeTestSignature> serialize_test, 219 std::function<DeserializeTestSignature> deserialize_test, 220 size_t data_size) { 221 tests_.emplace_back(name, std::move(serialize_test), 222 std::move(deserialize_test), data_size); 223 } 224 225 template <typename T> 226 void AddSerializationTest(const std::string& name, T&& value) { 227 const size_t data_size = GetSerializedSize(value); 228 auto serialize_test = 229 std::bind(static_cast<std::chrono::nanoseconds (*)( 230 MessageWriter*, size_t, ResetFunc*, void*, const T&)>( 231 &SerializeTestRunner), 232 _1, _2, _3, _4, std::forward<T>(value)); 233 tests_.emplace_back(name, std::move(serialize_test), 234 std::function<DeserializeTestSignature>{}, data_size); 235 } 236 237 template <typename T> 238 void AddDeserializationTest(const std::string& name, T&& value) { 239 const size_t data_size = GetSerializedSize(value); 240 auto deserialize_test = 241 std::bind(static_cast<std::chrono::nanoseconds (*)( 242 MessageReader*, MessageWriter*, size_t, ResetFunc*, 243 ResetFunc*, void*, const T&)>(&DeserializeTestRunner), 244 _1, _2, _3, _4, _5, _6, std::forward<T>(value)); 245 tests_.emplace_back(name, std::function<SerializeTestSignature>{}, 246 std::move(deserialize_test), data_size); 247 } 248 249 template <typename T> 250 void AddTest(const std::string& name, T&& value) { 251 const size_t data_size = GetSerializedSize(value); 252 if (data_size > kMaxStaticBufferSize) { 253 std::cerr << "Test '" << name << "' requires " << data_size 254 << " bytes in the serialization buffer but only " 255 << kMaxStaticBufferSize << " are available." << std::endl; 256 exit(1); 257 } 258 auto serialize_test = 259 std::bind(static_cast<std::chrono::nanoseconds (*)( 260 MessageWriter*, size_t, ResetFunc*, void*, const T&)>( 261 &SerializeTestRunner), 262 _1, _2, _3, _4, value); 263 auto deserialize_test = 264 std::bind(static_cast<std::chrono::nanoseconds (*)( 265 MessageReader*, MessageWriter*, size_t, ResetFunc*, 266 ResetFunc*, void*, const T&)>(&DeserializeTestRunner), 267 _1, _2, _3, _4, _5, _6, std::forward<T>(value)); 268 tests_.emplace_back(name, std::move(serialize_test), 269 std::move(deserialize_test), data_size); 270 } 271 272 std::string CenterString(std::string text, size_t column_width) { 273 if (text.size() < column_width) { 274 text = std::string((column_width - text.size()) / 2, ' ') + text; 275 } 276 return text; 277 } 278 279 void RunTests(size_t iteration_count, 280 const std::vector<BufferInfo>& buffers) { 281 using float_seconds = std::chrono::duration<double>; 282 const std::string name_column_separator = " : "; 283 const std::string buffer_column_separator = " || "; 284 const std::string buffer_timing_column_separator = " | "; 285 const size_t data_size_column_width = 6; 286 const size_t time_column_width = 9; 287 const size_t qps_column_width = 18; 288 const size_t buffer_column_width = time_column_width + 289 buffer_timing_column_separator.size() + 290 qps_column_width; 291 292 auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) { 293 return t1.name.size() < t2.name.size(); 294 }; 295 auto test_with_longest_name = 296 std::max_element(tests_.begin(), tests_.end(), compare_name_length); 297 size_t name_column_width = test_with_longest_name->name.size(); 298 299 size_t total_width = 300 name_column_width + name_column_separator.size() + 301 data_size_column_width + buffer_column_separator.size() + 302 buffers.size() * (buffer_column_width + buffer_column_separator.size()); 303 304 const std::string dbl_separator(total_width, '='); 305 const std::string separator(total_width, '-'); 306 307 auto print_header = [&](const std::string& header) { 308 std::cout << dbl_separator << std::endl; 309 std::stringstream ss; 310 ss.imbue(std::locale(ss.getloc(), new CommaNumPunct)); 311 ss << header << " (" << iteration_count << " iterations)"; 312 std::cout << CenterString(ss.str(), total_width) << std::endl; 313 std::cout << dbl_separator << std::endl; 314 std::cout << std::setw(name_column_width) << "Test Name" << std::left 315 << name_column_separator << std::setw(data_size_column_width) 316 << CenterString("Size", data_size_column_width) 317 << buffer_column_separator; 318 for (const auto& buffer_info : buffers) { 319 std::cout << std::setw(buffer_column_width) 320 << CenterString(buffer_info.name, buffer_column_width) 321 << buffer_column_separator; 322 } 323 std::cout << std::endl; 324 std::cout << std::setw(name_column_width) << "" << name_column_separator 325 << std::setw(data_size_column_width) 326 << CenterString("bytes", data_size_column_width) 327 << buffer_column_separator << std::left; 328 for (size_t i = 0; i < buffers.size(); i++) { 329 std::cout << std::setw(time_column_width) 330 << CenterString("Time, s", time_column_width) 331 << buffer_timing_column_separator 332 << std::setw(qps_column_width) 333 << CenterString("QPS", qps_column_width) 334 << buffer_column_separator; 335 } 336 std::cout << std::right << std::endl; 337 std::cout << separator << std::endl; 338 }; 339 340 print_header("Serialization benchmarks"); 341 for (const auto& test : tests_) { 342 if (test.serialize_test) { 343 std::cout << std::setw(name_column_width) << test.name << " : " 344 << std::setw(data_size_column_width) << test.data_size 345 << buffer_column_separator; 346 for (const auto& buffer_info : buffers) { 347 auto seconds = 348 std::chrono::duration_cast<float_seconds>(test.serialize_test( 349 buffer_info.writer, iteration_count, 350 buffer_info.write_reset_func, buffer_info.reset_data)); 351 double qps = iteration_count / seconds.count(); 352 std::cout << std::fixed << std::setprecision(3) 353 << std::setw(time_column_width) << seconds.count() 354 << buffer_timing_column_separator 355 << std::setw(qps_column_width) << qps 356 << buffer_column_separator; 357 } 358 std::cout << std::endl; 359 } 360 } 361 362 print_header("Deserialization benchmarks"); 363 for (const auto& test : tests_) { 364 if (test.deserialize_test) { 365 std::cout << std::setw(name_column_width) << test.name << " : " 366 << std::setw(data_size_column_width) << test.data_size 367 << buffer_column_separator; 368 for (const auto& buffer_info : buffers) { 369 auto seconds = 370 std::chrono::duration_cast<float_seconds>(test.deserialize_test( 371 buffer_info.reader, buffer_info.writer, iteration_count, 372 buffer_info.read_reset_func, buffer_info.write_reset_func, 373 buffer_info.reset_data)); 374 double qps = iteration_count / seconds.count(); 375 std::cout << std::fixed << std::setprecision(3) 376 << std::setw(time_column_width) << seconds.count() 377 << buffer_timing_column_separator 378 << std::setw(qps_column_width) << qps 379 << buffer_column_separator; 380 } 381 std::cout << std::endl; 382 } 383 } 384 std::cout << dbl_separator << std::endl; 385 } 386 387 private: 388 struct TestEntry { 389 TestEntry(const std::string& test_name, 390 std::function<SerializeTestSignature> serialize_test, 391 std::function<DeserializeTestSignature> deserialize_test, 392 size_t data_size) 393 : name{test_name}, 394 serialize_test{std::move(serialize_test)}, 395 deserialize_test{std::move(deserialize_test)}, 396 data_size{data_size} {} 397 std::string name; 398 std::function<SerializeTestSignature> serialize_test; 399 std::function<DeserializeTestSignature> deserialize_test; 400 size_t data_size; 401 }; 402 403 std::vector<TestEntry> tests_; 404 }; 405 406 std::string GenerateContainerName(const std::string& type, size_t count) { 407 std::stringstream ss; 408 ss << type << "(" << count << ")"; 409 return ss.str(); 410 } 411 412 } // anonymous namespace 413 414 int main(int /*argc*/, char** /*argv*/) { 415 const size_t iteration_count = 10000000; // 10M iterations. 416 TestRunner test_runner; 417 std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct)); 418 419 // Baseline tests to figure out the overhead of buffer resizing and data 420 // transfers. 421 for (size_t len : {0, 1, 9, 66, 259}) { 422 auto serialize_base_test = 423 std::bind(&SerializeBaseTest, _1, _2, _3, _4, len); 424 auto deserialize_base_test = 425 std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len); 426 test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test), 427 std::move(deserialize_base_test), len); 428 } 429 430 // Individual serialization/deserialization tests. 431 test_runner.AddTest("bool", true); 432 test_runner.AddTest("int32_t", 12); 433 434 for (size_t len : {0, 1, 8, 64, 256}) { 435 test_runner.AddTest(GenerateContainerName("string", len), 436 std::string(len, '*')); 437 } 438 // Serialization is too slow to handle such large strings, add this test for 439 // deserialization only. 440 test_runner.AddDeserializationTest(GenerateContainerName("string", 10240), 441 std::string(10240, '*')); 442 443 for (size_t len : {0, 1, 8, 64, 256}) { 444 std::vector<int32_t> int_vector(len); 445 std::iota(int_vector.begin(), int_vector.end(), 0); 446 test_runner.AddTest(GenerateContainerName("vector<int32_t>", len), 447 std::move(int_vector)); 448 } 449 450 std::vector<std::string> vector_of_strings = { 451 "012345678901234567890123456789", "012345678901234567890123456789", 452 "012345678901234567890123456789", "012345678901234567890123456789", 453 "012345678901234567890123456789", 454 }; 455 test_runner.AddTest( 456 GenerateContainerName("vector<string>", vector_of_strings.size()), 457 std::move(vector_of_strings)); 458 459 test_runner.AddTest("tuple<int, bool, string, double>", 460 std::make_tuple(123, true, std::string{"foobar"}, 1.1)); 461 462 for (size_t len : {0, 1, 8, 64}) { 463 std::map<int, std::string> test_map; 464 for (size_t i = 0; i < len; i++) 465 test_map.emplace(i, std::to_string(i)); 466 test_runner.AddTest(GenerateContainerName("map<int, string>", len), 467 std::move(test_map)); 468 } 469 470 for (size_t len : {0, 1, 8, 64}) { 471 std::unordered_map<int, std::string> test_map; 472 for (size_t i = 0; i < len; i++) 473 test_map.emplace(i, std::to_string(i)); 474 test_runner.AddTest( 475 GenerateContainerName("unordered_map<int, string>", len), 476 std::move(test_map)); 477 } 478 479 // BufferWrapper can't be used with deserialization tests right now because 480 // it requires external buffer to be filled in, which is not available. 481 std::vector<std::vector<uint8_t>> data_buffers; 482 for (size_t len : {0, 1, 8, 64, 256}) { 483 data_buffers.emplace_back(len); 484 test_runner.AddSerializationTest( 485 GenerateContainerName("BufferWrapper<uint8_t*>", len), 486 BufferWrapper<uint8_t*>(data_buffers.back().data(), 487 data_buffers.back().size())); 488 } 489 490 // Various backing buffers to run the tests on. 491 std::vector<TestRunner::BufferInfo> buffers; 492 493 Payload buffer; 494 buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer, 495 [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); }, 496 [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); }, 497 &buffer); 498 499 TestPayload tls_buffer; 500 buffers.emplace_back( 501 "TLS Buffer", &tls_buffer, &tls_buffer, 502 [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); }, 503 [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer); 504 505 StaticBuffer static_buffer; 506 buffers.emplace_back( 507 "Static Buffer", &static_buffer, &static_buffer, 508 [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); }, 509 [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); }, 510 &static_buffer); 511 512 // Finally, run all the tests. 513 test_runner.RunTests(iteration_count, buffers); 514 return 0; 515 } 516