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:
do_thousands_sep() const32   char do_thousands_sep() const override { return ','; }
do_grouping() const33   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
GetNextWriteBufferSection(size_t size)42   void* GetNextWriteBufferSection(size_t size) override {
43     const size_t section_offset = Size();
44     Extend(size);
45     return Data() + section_offset;
46   }
47 
GetOutputResourceMapper()48   OutputResourceMapper* GetOutputResourceMapper() override { return this; }
49 
50   // MessageReader
GetNextReadBufferSection()51   BufferSection GetNextReadBufferSection() override {
52     return {&*ConstCursor(), &*ConstEnd()};
53   }
54 
ConsumeReadBufferSectionData(const void * new_start)55   void ConsumeReadBufferSectionData(const void* new_start) override {
56     std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor()));
57   }
58 
GetInputResourceMapper()59   InputResourceMapper* GetInputResourceMapper() override { return this; }
60 };
61 
62 class StaticBuffer : public MessageWriter,
63                      public MessageReader,
64                      public NoOpResourceMapper {
65  public:
Clear()66   void Clear() {
67     read_ptr_ = buffer_;
68     write_ptr_ = 0;
69   }
Rewind()70   void Rewind() { read_ptr_ = buffer_; }
71 
72   // MessageWriter
GetNextWriteBufferSection(size_t size)73   void* GetNextWriteBufferSection(size_t size) override {
74     void* ptr = buffer_ + write_ptr_;
75     write_ptr_ += size;
76     return ptr;
77   }
78 
GetOutputResourceMapper()79   OutputResourceMapper* GetOutputResourceMapper() override { return this; }
80 
81   // MessageReader
GetNextReadBufferSection()82   BufferSection GetNextReadBufferSection() override {
83     return {read_ptr_, std::end(buffer_)};
84   }
85 
ConsumeReadBufferSectionData(const void * new_start)86   void ConsumeReadBufferSectionData(const void* new_start) override {
87     read_ptr_ = static_cast<const uint8_t*>(new_start);
88   }
89 
GetInputResourceMapper()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>
SerializeTestRunner(MessageWriter * writer,size_t iterations,ResetFunc * write_reset,void * reset_data,const T & value)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>
DeserializeTestRunner(MessageReader * reader,MessageWriter * writer,size_t iterations,ResetFunc * read_reset,ResetFunc * write_reset,void * reset_data,const T & value)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.
SerializeBaseTest(MessageWriter * writer,size_t iterations,ResetFunc * write_reset,void * reset_data,size_t data_size)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.
DeserializeBaseTest(MessageReader * reader,MessageWriter * writer,size_t iterations,ResetFunc * read_reset,ResetFunc * write_reset,void * reset_data,size_t data_size)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 {
BufferInfo__anonba883c880111::TestRunner::BufferInfo200     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 
AddTestFunc(const std::string & name,std::function<SerializeTestSignature> serialize_test,std::function<DeserializeTestSignature> deserialize_test,size_t data_size)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>
AddSerializationTest(const std::string & name,T && value)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>
AddDeserializationTest(const std::string & name,T && value)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>
AddTest(const std::string & name,T && value)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 
CenterString(std::string text,size_t column_width)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 
RunTests(size_t iteration_count,const std::vector<BufferInfo> & buffers)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 {
TestEntry__anonba883c880111::TestRunner::TestEntry389     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 
GenerateContainerName(const std::string & type,size_t count)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 
main(int,char **)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