benchmark.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright (c) 2009-2021, Google LLC
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are met:
  6. // * Redistributions of source code must retain the above copyright
  7. // notice, this list of conditions and the following disclaimer.
  8. // * Redistributions in binary form must reproduce the above copyright
  9. // notice, this list of conditions and the following disclaimer in the
  10. // documentation and/or other materials provided with the distribution.
  11. // * Neither the name of Google LLC nor the
  12. // names of its contributors may be used to endorse or promote products
  13. // derived from this software without specific prior written permission.
  14. //
  15. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. // ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
  19. // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. #include <benchmark/benchmark.h>
  26. #include <string.h>
  27. #include "absl/container/flat_hash_set.h"
  28. #include "benchmarks/descriptor.pb.h"
  29. #include "benchmarks/descriptor.upb.h"
  30. #include "benchmarks/descriptor.upbdefs.h"
  31. #include "benchmarks/descriptor_sv.pb.h"
  32. #include "google/ads/googleads/v7/services/google_ads_service.upbdefs.h"
  33. #include "google/protobuf/descriptor.pb.h"
  34. #include "google/protobuf/dynamic_message.h"
  35. #include "upb/def.hpp"
  36. upb_StringView descriptor = benchmarks_descriptor_proto_upbdefinit.descriptor;
  37. namespace protobuf = ::google::protobuf;
  38. /* A buffer big enough to parse descriptor.proto without going to heap. */
  39. char buf[65535];
  40. void CollectFileDescriptors(
  41. const _upb_DefPool_Init* file,
  42. std::vector<upb_StringView>& serialized_files,
  43. absl::flat_hash_set<const _upb_DefPool_Init*>& seen) {
  44. if (!seen.insert(file).second) return;
  45. for (_upb_DefPool_Init** deps = file->deps; *deps; deps++) {
  46. CollectFileDescriptors(*deps, serialized_files, seen);
  47. }
  48. serialized_files.push_back(file->descriptor);
  49. }
  50. static void BM_ArenaOneAlloc(benchmark::State& state) {
  51. for (auto _ : state) {
  52. upb_Arena* arena = upb_Arena_New();
  53. upb_Arena_Malloc(arena, 1);
  54. upb_Arena_Free(arena);
  55. }
  56. }
  57. BENCHMARK(BM_ArenaOneAlloc);
  58. static void BM_ArenaInitialBlockOneAlloc(benchmark::State& state) {
  59. for (auto _ : state) {
  60. upb_Arena* arena = upb_Arena_Init(buf, sizeof(buf), NULL);
  61. upb_Arena_Malloc(arena, 1);
  62. upb_Arena_Free(arena);
  63. }
  64. }
  65. BENCHMARK(BM_ArenaInitialBlockOneAlloc);
  66. enum LoadDescriptorMode {
  67. NoLayout,
  68. WithLayout,
  69. };
  70. // This function is mostly copied from upb/def.c, but it is modified to avoid
  71. // passing in the pre-generated mini-tables, in order to force upb to compute
  72. // them dynamically. Generally you would never want to do this, but we want to
  73. // simulate the cost we would pay if we were loading these types purely from
  74. // descriptors, with no mini-tales available.
  75. bool LoadDefInit_BuildLayout(upb_DefPool* s, const _upb_DefPool_Init* init,
  76. size_t* bytes) {
  77. _upb_DefPool_Init** deps = init->deps;
  78. google_protobuf_FileDescriptorProto* file;
  79. upb_Arena* arena;
  80. upb_Status status;
  81. upb_Status_Clear(&status);
  82. if (upb_DefPool_FindFileByName(s, init->filename)) {
  83. return true;
  84. }
  85. arena = upb_Arena_New();
  86. for (; *deps; deps++) {
  87. if (!LoadDefInit_BuildLayout(s, *deps, bytes)) goto err;
  88. }
  89. file = google_protobuf_FileDescriptorProto_parse_ex(
  90. init->descriptor.data, init->descriptor.size, NULL,
  91. kUpb_DecodeOption_AliasString, arena);
  92. *bytes += init->descriptor.size;
  93. if (!file) {
  94. upb_Status_SetErrorFormat(
  95. &status,
  96. "Failed to parse compiled-in descriptor for file '%s'. This should "
  97. "never happen.",
  98. init->filename);
  99. goto err;
  100. }
  101. // KEY DIFFERENCE: Here we pass in only the descriptor, and not the
  102. // pre-generated minitables.
  103. if (!upb_DefPool_AddFile(s, file, &status)) {
  104. goto err;
  105. }
  106. upb_Arena_Free(arena);
  107. return true;
  108. err:
  109. fprintf(stderr,
  110. "Error loading compiled-in descriptor for file '%s' (this should "
  111. "never happen): %s\n",
  112. init->filename, upb_Status_ErrorMessage(&status));
  113. exit(1);
  114. }
  115. template <LoadDescriptorMode Mode>
  116. static void BM_LoadAdsDescriptor_Upb(benchmark::State& state) {
  117. size_t bytes_per_iter = 0;
  118. for (auto _ : state) {
  119. upb::SymbolTable symtab;
  120. if (Mode == NoLayout) {
  121. google_ads_googleads_v7_services_SearchGoogleAdsRequest_getmsgdef(
  122. symtab.ptr());
  123. bytes_per_iter = _upb_DefPool_BytesLoaded(symtab.ptr());
  124. } else {
  125. bytes_per_iter = 0;
  126. LoadDefInit_BuildLayout(
  127. symtab.ptr(),
  128. &google_ads_googleads_v7_services_google_ads_service_proto_upbdefinit,
  129. &bytes_per_iter);
  130. }
  131. }
  132. state.SetBytesProcessed(state.iterations() * bytes_per_iter);
  133. }
  134. BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Upb, NoLayout);
  135. BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Upb, WithLayout);
  136. template <LoadDescriptorMode Mode>
  137. static void BM_LoadAdsDescriptor_Proto2(benchmark::State& state) {
  138. extern _upb_DefPool_Init
  139. google_ads_googleads_v7_services_google_ads_service_proto_upbdefinit;
  140. std::vector<upb_StringView> serialized_files;
  141. absl::flat_hash_set<const _upb_DefPool_Init*> seen_files;
  142. CollectFileDescriptors(
  143. &google_ads_googleads_v7_services_google_ads_service_proto_upbdefinit,
  144. serialized_files, seen_files);
  145. size_t bytes_per_iter = 0;
  146. for (auto _ : state) {
  147. bytes_per_iter = 0;
  148. protobuf::Arena arena;
  149. protobuf::DescriptorPool pool;
  150. for (auto file : serialized_files) {
  151. protobuf::StringPiece input(file.data, file.size);
  152. auto proto =
  153. protobuf::Arena::CreateMessage<protobuf::FileDescriptorProto>(&arena);
  154. bool ok = proto->ParseFrom<protobuf::MessageLite::kMergePartial>(input) &&
  155. pool.BuildFile(*proto) != nullptr;
  156. if (!ok) {
  157. printf("Failed to add file.\n");
  158. exit(1);
  159. }
  160. bytes_per_iter += input.size();
  161. }
  162. if (Mode == WithLayout) {
  163. protobuf::DynamicMessageFactory factory;
  164. const protobuf::Descriptor* d = pool.FindMessageTypeByName(
  165. "google.ads.googleads.v7.services.SearchGoogleAdsResponse");
  166. if (!d) {
  167. printf("Failed to find descriptor.\n");
  168. exit(1);
  169. }
  170. factory.GetPrototype(d);
  171. }
  172. }
  173. state.SetBytesProcessed(state.iterations() * bytes_per_iter);
  174. }
  175. BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Proto2, NoLayout);
  176. BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Proto2, WithLayout);
  177. enum CopyStrings {
  178. Copy,
  179. Alias,
  180. };
  181. enum ArenaMode {
  182. NoArena,
  183. UseArena,
  184. InitBlock,
  185. };
  186. template <ArenaMode AMode, CopyStrings Copy>
  187. static void BM_Parse_Upb_FileDesc(benchmark::State& state) {
  188. for (auto _ : state) {
  189. upb_Arena* arena;
  190. if (AMode == InitBlock) {
  191. arena = upb_Arena_Init(buf, sizeof(buf), NULL);
  192. } else {
  193. arena = upb_Arena_New();
  194. }
  195. upb_benchmark_FileDescriptorProto* set =
  196. upb_benchmark_FileDescriptorProto_parse_ex(
  197. descriptor.data, descriptor.size, NULL,
  198. Copy == Alias ? kUpb_DecodeOption_AliasString : 0, arena);
  199. if (!set) {
  200. printf("Failed to parse.\n");
  201. exit(1);
  202. }
  203. upb_Arena_Free(arena);
  204. }
  205. state.SetBytesProcessed(state.iterations() * descriptor.size);
  206. }
  207. BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Copy);
  208. BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Alias);
  209. BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Copy);
  210. BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Alias);
  211. template <ArenaMode AMode, class P>
  212. struct Proto2Factory;
  213. template <class P>
  214. struct Proto2Factory<NoArena, P> {
  215. public:
  216. P* GetProto() { return &proto_; }
  217. private:
  218. P proto_;
  219. };
  220. template <class P>
  221. struct Proto2Factory<UseArena, P> {
  222. public:
  223. P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); }
  224. private:
  225. protobuf::Arena arena_;
  226. };
  227. template <class P>
  228. struct Proto2Factory<InitBlock, P> {
  229. public:
  230. Proto2Factory() : arena_(GetOptions()) {}
  231. P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); }
  232. private:
  233. protobuf::ArenaOptions GetOptions() {
  234. protobuf::ArenaOptions opts;
  235. opts.initial_block = buf;
  236. opts.initial_block_size = sizeof(buf);
  237. return opts;
  238. }
  239. protobuf::Arena arena_;
  240. };
  241. using FileDesc = ::upb_benchmark::FileDescriptorProto;
  242. using FileDescSV = ::upb_benchmark::sv::FileDescriptorProto;
  243. template <class P, ArenaMode AMode, CopyStrings kCopy>
  244. void BM_Parse_Proto2(benchmark::State& state) {
  245. constexpr protobuf::MessageLite::ParseFlags kParseFlags =
  246. kCopy == Copy
  247. ? protobuf::MessageLite::ParseFlags::kMergePartial
  248. : protobuf::MessageLite::ParseFlags::kMergePartialWithAliasing;
  249. for (auto _ : state) {
  250. Proto2Factory<AMode, P> proto_factory;
  251. auto proto = proto_factory.GetProto();
  252. protobuf::StringPiece input(descriptor.data, descriptor.size);
  253. bool ok = proto->template ParseFrom<kParseFlags>(input);
  254. if (!ok) {
  255. printf("Failed to parse.\n");
  256. exit(1);
  257. }
  258. }
  259. state.SetBytesProcessed(state.iterations() * descriptor.size);
  260. }
  261. BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, NoArena, Copy);
  262. BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, UseArena, Copy);
  263. BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, InitBlock, Copy);
  264. BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, InitBlock, Alias);
  265. static void BM_SerializeDescriptor_Proto2(benchmark::State& state) {
  266. upb_benchmark::FileDescriptorProto proto;
  267. proto.ParseFromArray(descriptor.data, descriptor.size);
  268. for (auto _ : state) {
  269. proto.SerializePartialToArray(buf, sizeof(buf));
  270. }
  271. state.SetBytesProcessed(state.iterations() * descriptor.size);
  272. }
  273. BENCHMARK(BM_SerializeDescriptor_Proto2);
  274. static void BM_SerializeDescriptor_Upb(benchmark::State& state) {
  275. int64_t total = 0;
  276. upb_Arena* arena = upb_Arena_New();
  277. upb_benchmark_FileDescriptorProto* set =
  278. upb_benchmark_FileDescriptorProto_parse(descriptor.data, descriptor.size,
  279. arena);
  280. if (!set) {
  281. printf("Failed to parse.\n");
  282. exit(1);
  283. }
  284. for (auto _ : state) {
  285. upb_Arena* enc_arena = upb_Arena_Init(buf, sizeof(buf), NULL);
  286. size_t size;
  287. char* data =
  288. upb_benchmark_FileDescriptorProto_serialize(set, enc_arena, &size);
  289. if (!data) {
  290. printf("Failed to serialize.\n");
  291. exit(1);
  292. }
  293. total += size;
  294. }
  295. state.SetBytesProcessed(total);
  296. }
  297. BENCHMARK(BM_SerializeDescriptor_Upb);