Added API for copy vs. alias and added benchmarks to test both.

Benchmark output:

$ bazel-bin/benchmarks/benchmark '--benchmark_filter=BM_Parse'
2020-11-11 15:39:04
Running bazel-bin/benchmarks/benchmark
Run on (72 X 3700 MHz CPU s)
CPU Caches:
  L1 Data 32K (x36)
  L1 Instruction 32K (x36)
  L2 Unified 1024K (x36)
  L3 Unified 25344K (x2)
-------------------------------------------------------------------------------------
Benchmark                                              Time           CPU Iterations
-------------------------------------------------------------------------------------
BM_Parse_Upb_FileDesc<UseArena, Copy>               4134 ns       4134 ns     168714   1.69152GB/s
BM_Parse_Upb_FileDesc<UseArena, Alias>              3487 ns       3487 ns     199509   2.00526GB/s
BM_Parse_Upb_FileDesc<InitBlock, Copy>              3727 ns       3726 ns     187581   1.87643GB/s
BM_Parse_Upb_FileDesc<InitBlock, Alias>             3110 ns       3110 ns     224970   2.24866GB/s
BM_Parse_Proto2<FileDesc, NoArena, Copy>           31132 ns      31132 ns      22437   229.995MB/s
BM_Parse_Proto2<FileDesc, UseArena, Copy>          21011 ns      21009 ns      33922   340.812MB/s
BM_Parse_Proto2<FileDesc, InitBlock, Copy>         17976 ns      17975 ns      38808   398.337MB/s
BM_Parse_Proto2<FileDescSV, InitBlock, Alias>      17357 ns      17356 ns      40244   412.539MB/s
pull/13171/head
Joshua Haberman 4 years ago
parent 27b9c999a7
commit 65d166a6ba
  1. 87
      benchmarks/benchmark.cc
  2. 9
      upb/decode.c
  3. 19
      upb/decode.h
  4. 2
      upb/decode_fast.c
  5. 4
      upb/def.c
  6. 6
      upbc/generator.cc

@ -115,31 +115,31 @@ static void BM_LoadAdsDescriptor_Proto2(benchmark::State& state) {
} }
BENCHMARK(BM_LoadAdsDescriptor_Proto2); BENCHMARK(BM_LoadAdsDescriptor_Proto2);
static void BM_Parse_Upb_FileDesc_WithArena(benchmark::State& state) { enum CopyStrings {
size_t bytes = 0; Copy,
for (auto _ : state) { Alias,
upb_arena* arena = upb_arena_new(); };
upb_benchmark_FileDescriptorProto* set =
upb_benchmark_FileDescriptorProto_parse(descriptor.data, enum ArenaMode {
descriptor.size, arena); NoArena,
if (!set) { UseArena,
printf("Failed to parse.\n"); InitBlock,
exit(1); };
}
bytes += descriptor.size;
upb_arena_free(arena);
}
state.SetBytesProcessed(state.iterations() * descriptor.size);
}
BENCHMARK(BM_Parse_Upb_FileDesc_WithArena);
static void BM_Parse_Upb_FileDesc_WithInitialBlock(benchmark::State& state) { template <ArenaMode AMode, CopyStrings Copy>
static void BM_Parse_Upb_FileDesc(benchmark::State& state) {
size_t bytes = 0; size_t bytes = 0;
for (auto _ : state) { for (auto _ : state) {
upb_arena* arena = upb_arena_init(buf, sizeof(buf), NULL); upb_arena *arena;
if (AMode == InitBlock) {
arena = upb_arena_init(buf, sizeof(buf), NULL);
} else {
arena = upb_arena_new();
}
upb_benchmark_FileDescriptorProto* set = upb_benchmark_FileDescriptorProto* set =
upb_benchmark_FileDescriptorProto_parse(descriptor.data, upb_benchmark_FileDescriptorProto_parse_ex(
descriptor.size, arena); descriptor.data, descriptor.size, arena,
Copy == Alias ? UPB_DECODE_ALIAS : 0);
if (!set) { if (!set) {
printf("Failed to parse.\n"); printf("Failed to parse.\n");
exit(1); exit(1);
@ -149,10 +149,16 @@ static void BM_Parse_Upb_FileDesc_WithInitialBlock(benchmark::State& state) {
} }
state.SetBytesProcessed(state.iterations() * descriptor.size); state.SetBytesProcessed(state.iterations() * descriptor.size);
} }
BENCHMARK(BM_Parse_Upb_FileDesc_WithInitialBlock); BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Alias);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Alias);
template <class P> template <ArenaMode AMode, class P>
struct NoArena { struct Proto2Factory;
template<class P>
struct Proto2Factory<NoArena, P> {
public: public:
P* GetProto() { return &proto_; } P* GetProto() { return &proto_; }
@ -161,7 +167,7 @@ struct NoArena {
}; };
template <class P> template <class P>
struct WithArena { struct Proto2Factory<UseArena, P> {
public: public:
P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); } P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); }
@ -170,9 +176,9 @@ struct WithArena {
}; };
template <class P> template <class P>
struct WithInitialBlock { struct Proto2Factory<InitBlock, P> {
public: public:
WithInitialBlock() : arena_(GetOptions()) {} Proto2Factory() : arena_(GetOptions()) {}
P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); } P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena_); }
private: private:
@ -189,17 +195,15 @@ struct WithInitialBlock {
using FileDesc = ::upb_benchmark::FileDescriptorProto; using FileDesc = ::upb_benchmark::FileDescriptorProto;
using FileDescSV = ::upb_benchmark::sv::FileDescriptorProto; using FileDescSV = ::upb_benchmark::sv::FileDescriptorProto;
const protobuf::MessageLite::ParseFlags kMergePartial = template <class P, ArenaMode AMode, CopyStrings kCopy>
protobuf::MessageLite::ParseFlags::kMergePartial;
const protobuf::MessageLite::ParseFlags kAlias =
protobuf::MessageLite::ParseFlags::kMergePartialWithAliasing;
template <class P, template <class> class Factory,
protobuf::MessageLite::ParseFlags kParseFlags = kMergePartial>
void BM_Parse_Proto2(benchmark::State& state) { void BM_Parse_Proto2(benchmark::State& state) {
size_t bytes = 0; size_t bytes = 0;
constexpr protobuf::MessageLite::ParseFlags kParseFlags =
kCopy == Copy
? protobuf::MessageLite::ParseFlags::kMergePartial
: protobuf::MessageLite::ParseFlags::kMergePartialWithAliasing;
for (auto _ : state) { for (auto _ : state) {
Factory<P> proto_factory; Proto2Factory<AMode, P> proto_factory;
auto proto = proto_factory.GetProto(); auto proto = proto_factory.GetProto();
protobuf::StringPiece input(descriptor.data,descriptor.size); protobuf::StringPiece input(descriptor.data,descriptor.size);
bool ok = proto->template ParseFrom<kParseFlags>(input); bool ok = proto->template ParseFrom<kParseFlags>(input);
@ -211,15 +215,10 @@ void BM_Parse_Proto2(benchmark::State& state) {
} }
state.SetBytesProcessed(state.iterations() * descriptor.size); state.SetBytesProcessed(state.iterations() * descriptor.size);
} }
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, NoArena); BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, NoArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, WithArena); BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, UseArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, WithInitialBlock); BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, InitBlock, Copy);
//BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, NoArena); BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, InitBlock, Alias);
//BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, WithArena);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, WithInitialBlock);
//BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, NoArena, kAlias);
//BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, WithArena, kAlias);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, WithInitialBlock, kAlias);
static void BM_SerializeDescriptor_Proto2(benchmark::State& state) { static void BM_SerializeDescriptor_Proto2(benchmark::State& state) {
size_t bytes = 0; size_t bytes = 0;

@ -643,10 +643,11 @@ const char *fastdecode_generic(struct upb_decstate *d, const char *ptr,
return decode_msg(d, ptr, msg, decode_totablep(table)); return decode_msg(d, ptr, msg, decode_totablep(table));
} }
bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l, bool _upb_decode(const char *buf, size_t size, void *msg,
upb_arena *arena) { const upb_msglayout *l, upb_arena *arena, int options) {
bool ok; bool ok;
upb_decstate state; upb_decstate state;
unsigned depth = (unsigned)options >> 16;
if (size == 0) { if (size == 0) {
return true; return true;
@ -660,12 +661,12 @@ bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
} else { } else {
state.end = buf + size - 16; state.end = buf + size - 16;
state.limit = 16; state.limit = 16;
state.alias = true; state.alias = options & UPB_DECODE_ALIAS;
} }
state.limit_ptr = state.end; state.limit_ptr = state.end;
state.unknown_msg = NULL; state.unknown_msg = NULL;
state.depth = 64; state.depth = depth ? depth : 64;
state.end_group = DECODE_NOGROUP; state.end_group = DECODE_NOGROUP;
state.arena.head = arena->head; state.arena.head = arena->head;
state.arena.last_size = arena->last_size; state.arena.last_size = arena->last_size;

@ -7,15 +7,32 @@
#include "upb/msg.h" #include "upb/msg.h"
/* Must be last. */
#include "upb/port_def.inc"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
enum {
UPB_DECODE_ALIAS = 1,
};
#define UPB_DECODE_MAXDEPTH(depth) ((depth) << 16)
bool _upb_decode(const char *buf, size_t size, upb_msg *msg,
const upb_msglayout *l, upb_arena *arena, int options);
UPB_INLINE
bool upb_decode(const char *buf, size_t size, upb_msg *msg, bool upb_decode(const char *buf, size_t size, upb_msg *msg,
const upb_msglayout *l, upb_arena *arena); const upb_msglayout *l, upb_arena *arena) {
return _upb_decode(buf, size, msg, l, arena, 0);
}
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif
#include "upb/port_undef.inc"
#endif /* UPB_DECODE_H_ */ #endif /* UPB_DECODE_H_ */

@ -763,7 +763,7 @@ again:
} else if (UPB_LIKELY(size <= 64)) { } else if (UPB_LIKELY(size <= 64)) {
if (UPB_UNLIKELY(common_has < 64)) goto longstr; if (UPB_UNLIKELY(common_has < 64)) goto longstr;
fastdecode_docopy(d, ptr, size, 64, buf, dst); fastdecode_docopy(d, ptr, size, 64, buf, dst);
} else if (UPB_LIKELY(size <= 128)) { } else if (UPB_LIKELY(size < 128)) {
if (UPB_UNLIKELY(common_has < 128)) goto longstr; if (UPB_UNLIKELY(common_has < 128)) goto longstr;
fastdecode_docopy(d, ptr, size, 128, buf, dst); fastdecode_docopy(d, ptr, size, 128, buf, dst);
} else { } else {

@ -2140,8 +2140,8 @@ bool _upb_symtab_loaddefinit(upb_symtab *s, const upb_def_init *init) {
if (!_upb_symtab_loaddefinit(s, *deps)) goto err; if (!_upb_symtab_loaddefinit(s, *deps)) goto err;
} }
file = google_protobuf_FileDescriptorProto_parse( file = google_protobuf_FileDescriptorProto_parse_ex(
init->descriptor.data, init->descriptor.size, arena); init->descriptor.data, init->descriptor.size, arena, UPB_DECODE_ALIAS);
s->bytes_loaded += init->descriptor.size; s->bytes_loaded += init->descriptor.size;
if (!file) { if (!file) {

@ -348,6 +348,12 @@ void GenerateMessageInHeader(const protobuf::Descriptor* message, Output& output
" $0 *ret = $0_new(arena);\n" " $0 *ret = $0_new(arena);\n"
" return (ret && upb_decode(buf, size, ret, &$1, arena)) ? ret : NULL;\n" " return (ret && upb_decode(buf, size, ret, &$1, arena)) ? ret : NULL;\n"
"}\n" "}\n"
"UPB_INLINE $0 *$0_parse_ex(const char *buf, size_t size,\n"
" upb_arena *arena, int options) {\n"
" $0 *ret = $0_new(arena);\n"
" return (ret && _upb_decode(buf, size, ret, &$1, arena, options))\n"
" ? ret : NULL;\n"
"}\n"
"UPB_INLINE char *$0_serialize(const $0 *msg, upb_arena *arena, size_t " "UPB_INLINE char *$0_serialize(const $0 *msg, upb_arena *arena, size_t "
"*len) {\n" "*len) {\n"
" return upb_encode(msg, &$1, arena, len);\n" " return upb_encode(msg, &$1, arena, len);\n"

Loading…
Cancel
Save