Skip to content

Commit

Permalink
Optimization for checking enum fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
haberman committed Sep 21, 2021
1 parent 7ba22dd commit d4ff572
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 83 deletions.
75 changes: 15 additions & 60 deletions cmake/google/protobuf/descriptor.upb.c
Original file line number Diff line number Diff line change
Expand Up @@ -518,85 +518,40 @@ static const upb_msglayout *messages_layout[27] = {
&google_protobuf_GeneratedCodeInfo_Annotation_msginit,
};

static const int32_t google_protobuf_FieldDescriptorProto_Type_enuminit_values[18] = {
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
};

const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit = {
google_protobuf_FieldDescriptorProto_Type_enuminit_values,
18,
};

static const int32_t google_protobuf_FieldDescriptorProto_Label_enuminit_values[3] = {
1,
2,
3,
NULL,
0x7fffeULL,
0,
};

const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit = {
google_protobuf_FieldDescriptorProto_Label_enuminit_values,
3,
};

static const int32_t google_protobuf_FileOptions_OptimizeMode_enuminit_values[3] = {
1,
2,
3,
NULL,
0x7fffeULL,
0,
};

const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit = {
google_protobuf_FileOptions_OptimizeMode_enuminit_values,
3,
};

static const int32_t google_protobuf_FieldOptions_CType_enuminit_values[3] = {
NULL,
0x7fffeULL,
0,
1,
2,
};

const upb_enumlayout google_protobuf_FieldOptions_CType_enuminit = {
google_protobuf_FieldOptions_CType_enuminit_values,
3,
};

static const int32_t google_protobuf_FieldOptions_JSType_enuminit_values[3] = {
NULL,
0x7ffffULL,
0,
1,
2,
};

const upb_enumlayout google_protobuf_FieldOptions_JSType_enuminit = {
google_protobuf_FieldOptions_JSType_enuminit_values,
3,
};

static const int32_t google_protobuf_MethodOptions_IdempotencyLevel_enuminit_values[3] = {
NULL,
0x7ffffULL,
0,
1,
2,
};

const upb_enumlayout google_protobuf_MethodOptions_IdempotencyLevel_enuminit = {
google_protobuf_MethodOptions_IdempotencyLevel_enuminit_values,
3,
NULL,
0x7ffffULL,
0,
};

static const upb_enumlayout *enums_layout[6] = {
Expand Down
41 changes: 30 additions & 11 deletions upb/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,15 @@ static char *encode_varint32(uint32_t val, char *ptr) {
return ptr;
}

static bool decode_checkenum(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_msglayout_sub *subs,
const upb_msglayout_field *field, wireval *val) {
const upb_enumlayout *e = subs[field->submsg_index].subenum;
uint32_t v = val->uint32_val;

if (_upb_enumlayout_checkval(e, v)) return true;
UPB_NOINLINE
static bool decode_checkenum_slow(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_enumlayout *e,
const upb_msglayout_field *field, uint32_t v) {
// OPT: binary search long lists?
int n = e->value_count;
for (int i = 0; i < n; i++) {
if ((uint32_t)e->values[i] == v) return true;
}

// Unrecognized enum goes into unknown fields.
char buf[20];
Expand All @@ -434,6 +436,17 @@ static bool decode_checkenum(upb_decstate *d, const char *ptr, upb_msg *msg,
return false;
}

UPB_FORCEINLINE
static bool decode_checkenum(upb_decstate *d, const char *ptr, upb_msg *msg,
const upb_enumlayout *e,
const upb_msglayout_field *field, wireval *val) {
uint32_t v = val->uint32_val;

if (v < 64 && ((1 << v) & e->mask)) return true;

return decode_checkenum_slow(d, ptr, msg, e, field, v);
}

static const char *decode_toarray(upb_decstate *d, const char *ptr,
upb_msg *msg,
const upb_msglayout_sub *subs,
Expand All @@ -443,6 +456,7 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
upb_array *arr = *arrp;
void *mem;
int lg2;
const upb_enumlayout *e;

if (arr) {
decode_reserve(d, arr, 1);
Expand All @@ -454,10 +468,12 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
}

switch (op) {
case OP_ENUM:
if (!decode_checkenum(d, ptr, msg, subs, field, val)) return ptr;
case OP_ENUM: {
const upb_enumlayout *e = subs[field->submsg_index].subenum;
if (!decode_checkenum(d, ptr, msg, e, field, val)) return ptr;
op = OP_SCALAR_LG2(2);
/* Fallthrough. */
}
case OP_SCALAR_LG2(0):
case OP_SCALAR_LG2(2):
case OP_SCALAR_LG2(3):
Expand Down Expand Up @@ -504,6 +520,7 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
}
case OP_PACKED_ENUM:
lg2 = 2;
e = subs[field->submsg_index].subenum;
goto packed;
/* Fallthrough */
case OP_VARPCK_LG2(0):
Expand All @@ -521,7 +538,7 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
ptr = decode_varint64(d, ptr, &elem.uint64_val);
decode_munge(field->descriptortype, &elem);
if (op == OP_PACKED_ENUM &&
!decode_checkenum(d, ptr, msg, subs, field, &elem)) {
!decode_checkenum(d, ptr, msg, e, field, &elem)) {
continue;
}
if (decode_reserve(d, arr, 1)) {
Expand Down Expand Up @@ -580,7 +597,9 @@ static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
void *mem = UPB_PTR_AT(msg, field->offset, void);
int type = field->descriptortype;

if (op == OP_ENUM && !decode_checkenum(d, ptr, msg, subs, field, val)) {
if (op == OP_ENUM &&
!decode_checkenum(d, ptr, msg, subs[field->submsg_index].subenum, field,
val)) {
return ptr;
}

Expand Down
24 changes: 21 additions & 3 deletions upb/def.c
Original file line number Diff line number Diff line change
Expand Up @@ -2228,15 +2228,33 @@ static void create_service(
}

upb_enumlayout *create_enumlayout(symtab_addctx *ctx, const upb_enumdef *e) {
int n = e->value_count;
int n = 0;
uint64_t mask = 0;

for (int i = 0; i < n; i++) {
uint32_t val = (uint32_t)e->values[i].number;
if (val < 64) {
mask |= 1 << val;
} else {
n++;
}
}

int32_t *values = symtab_alloc(ctx, sizeof(*values) * n);
int32_t *p = values;

for (int i = 0; i < n; i++) {
values[i] = e->values[i].number;
int32_t val = e->values[i].number;
if ((uint32_t)val < 64) {
*p++ = val;
}
}

UPB_ASSERT(p == values + n);

upb_enumlayout *layout = symtab_alloc(ctx, sizeof(*layout));
layout->value_count = n;
layout->mask = mask;
layout->values = values;

return layout;
Expand Down Expand Up @@ -2306,7 +2324,7 @@ static void create_enumdef(
if (ctx->layout) {
UPB_ASSERT(ctx->enum_count < ctx->layout->enum_count);
e->layout = ctx->layout->enums[ctx->enum_count++];
UPB_ASSERT(n == e->layout->value_count);
UPB_ASSERT(n == e->layout->value_count + __builtin_popcountll(e->layout->mask));
} else {
e->layout = create_enumlayout(ctx, e);
}
Expand Down
7 changes: 5 additions & 2 deletions upb/msg_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,15 @@ typedef struct {
} _upb_fasttable_entry;

typedef struct {
const int32_t *values;
const int32_t *values; // List of values <0 or >63
uint64_t mask; // Bits are set for acceptable value 0 <= x < 64
int value_count;
} upb_enumlayout;

UPB_INLINE bool _upb_enumlayout_checkval(const upb_enumlayout *e, int32_t val) {
// OPT: dense_below, binary search long lists?
uint32_t uval = (uint32_t)val;
if (uval < 64) return e->mask & (1 << uval);
// OPT: binary search long lists?
int n = e->value_count;
for (int i = 0; i < n; i++) {
if (e->values[i] == val) return true;
Expand Down
26 changes: 19 additions & 7 deletions upbc/protoc-gen-upb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1182,23 +1182,35 @@ int WriteEnums(const protobuf::FileDescriptor* file, Output& output) {
std::vector<const protobuf::EnumDescriptor*> this_file_enums =
SortedEnums(file);

std::string values_init = "NULL";

for (auto e : this_file_enums) {
uint64_t mask = 0;
absl::flat_hash_set<int32_t> values;
for (int i = 0; i < e->value_count(); i++) {
values.insert(e->value(i)->number());
int32_t number = e->value(i)->number();
if (static_cast<uint32_t>(number) < 64) {
mask |= 1 << number;
} else {
values.insert(number);
}
}
std::vector<int32_t> values_vec(values.begin(), values.end());
std::sort(values_vec.begin(), values_vec.end());

output("static const int32_t $0_values[$1] = {\n", EnumInit(e),
values_vec.size());
for (auto value : values_vec) {
output(" $0,\n", value);
if (!values_vec.empty()) {
values_init = EnumInit(e) + "_values";
output("static const int32_t $0[$1] = {\n", values_init,
values_vec.size());
for (auto value : values_vec) {
output(" $0,\n", value);
}
output("};\n\n");
}
output("};\n\n");

output("const upb_enumlayout $0 = {\n", EnumInit(e));
output(" $0_values,\n", EnumInit(e));
output(" $0,\n", values_init);
output(" 0x$0ULL,\n", absl::Hex(mask));
output(" $0,\n", values_vec.size());

output("};\n\n");
Expand Down

0 comments on commit d4ff572

Please sign in to comment.