encoder_performance_test.cpp 20 KB

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