Performance improvements.

pull/13171/head
Joshua Haberman 16 years ago
parent 528cdd2ecd
commit a13873276b
  1. 17
      benchmark/benchmark.cc
  2. 111
      src/upb_parse.c

@ -43,7 +43,7 @@ int main ()
size_t total = 0;
clock_t before = clock();
for(int i = 0; i < 2000; i++) {
upb_msg_parse_reset(&s, data, m, false, true);
upb_msg_parse_reset(&s, data, m, false, false);
upb_status_t status = upb_msg_parse(&s, str.ptr, str.byte_len, &read);
if(status != UPB_STATUS_OK && read != str.byte_len) {
fprintf(stderr, "Error. :( error=%d, read=%lu\n", status, read);
@ -54,6 +54,21 @@ int main ()
double elapsed = ((double)clock() - before) / CLOCKS_PER_SEC;
fprintf(stderr, "upb parsed %sB, ", eng(total, 3, false));
fprintf(stderr, "%sB/s\n", eng(total/elapsed, 3, false));
total = 0;
before = clock();
for(int i = 0; i < 2000; i++) {
upb_msg_parse_reset(&s, data, m, false, true);
upb_status_t status = upb_msg_parse(&s, str.ptr, str.byte_len, &read);
if(status != UPB_STATUS_OK && read != str.byte_len) {
fprintf(stderr, "Error. :( error=%d, read=%lu\n", status, read);
return 1;
}
total += str.byte_len;
}
elapsed = ((double)clock() - before) / CLOCKS_PER_SEC;
fprintf(stderr, "upb(byref) parsed %sB, ", eng(total, 3, false));
fprintf(stderr, "%sB/s\n", eng(total/elapsed, 3, false));
upb_msg_parse_free(&s);
upb_msgdata_free(data, m, true);
upb_context_free(&c);

@ -27,20 +27,53 @@ static void *check_end(uint8_t *buf, void *end, size_t maxlen,
}
}
static upb_status_t get_v_uint64_t(void *restrict *buf, void *end,
inline static upb_status_t get_v_uint64_t(void *restrict *buf, void *end,
uint64_t *restrict val)
{
uint8_t *b = *buf;
upb_status_t bound_error;
end = check_end(b, end, 10, &bound_error); /* 2**64 is a 10-byte varint. */
uint8_t last = 0x80;
*val = 0;
for(int bitpos = 0; b < (uint8_t*)end && (last & 0x80); b++, bitpos += 7)
*val |= ((uint64_t)((last = *b) & 0x7F)) << bitpos;
if(last & 0x80) return bound_error;
*buf = b;
return UPB_STATUS_OK;
if((*b & 0x80) == 0) {
/* Single-byte varint -- very common case. */
*buf = b + 1;
*val = *b & 0x7f;
return UPB_STATUS_OK;
} else if(b <= (uint8_t*)end && (*(b+1) & 0x80) == 0) {
/* Two-byte varint. */
*buf = b + 2;
*val = (b[0] & 0x7f) | ((b[1] & 0x7f) << 7);
return UPB_STATUS_OK;
} else if(b + 10 <= (uint8_t*)end) {
/* >2-byte varint, fast path. */
uint64_t cont = *(uint64_t*)(b+2) | 0x7f7f7f7f7f7f7f7fULL;
int num_bytes = __builtin_ffsll(~cont) / 8;
uint32_t part0 = 0, part1 = 0, part2 = 0;
switch(num_bytes) {
default: return UPB_ERROR_UNTERMINATED_VARINT;
case 8: part2 |= (b[9] & 0x7F) << 7;
case 7: part2 |= (b[8] & 0x7F);
case 6: part1 |= (b[7] & 0x7F) << 21;
case 5: part1 |= (b[6] & 0x7F) << 14;
case 4: part1 |= (b[5] & 0x7F) << 7;
case 3: part1 |= (b[4] & 0x7F);
case 2: part0 |= (b[3] & 0x7F) << 21;
case 1: part0 |= (b[2] & 0x7F) << 14;
part0 |= (b[1] & 0x7F) << 7;
part0 |= (b[0] & 0x7F);
}
*buf = b + num_bytes + 2;
*val = (uint64_t)part0 | ((uint64_t)part1 << 28) | ((uint64_t)part2 << 56);
return UPB_STATUS_OK;
} else {
/* >2-byte varint, slow path. */
uint8_t last = 0x80;
*val = 0;
for(int bitpos = 0; b < (uint8_t*)end && (last & 0x80); b++, bitpos += 7)
*val |= ((uint64_t)((last = *b) & 0x7F)) << bitpos;
if(last & 0x80) return UPB_STATUS_NEED_MORE_DATA;
*buf = b;
return UPB_STATUS_OK;
}
}
static upb_status_t skip_v_uint64_t(void **buf, void *end)
@ -60,22 +93,9 @@ static upb_status_t skip_v_uint64_t(void **buf, void *end)
static upb_status_t get_v_uint32_t(void *restrict *buf, void *end,
uint32_t *restrict val)
{
uint8_t *b = *buf, *dend;
upb_status_t bound_error;
dend = check_end(b, end, 5, &bound_error); /* 2**32 is a 5-byte varint. */
end = check_end(b, end, 10, &bound_error); /* May have to discard bytes. */
uint8_t last = 0x80;
*val = 0;
for(int bitpos = 0; b < dend && (last & 0x80); b++, bitpos += 7)
*val |= ((uint32_t)((last = *b) & 0x7F)) << bitpos;
/* Discard high bytes until varint ends. */
for(; b < (uint8_t*)end && (last & 0x80); b++)
last = *b;
if(last & 0x80)
return bound_error;
*buf = b;
uint64_t val64;
UPB_CHECK(get_v_uint64_t(buf, end, &val64));
*val = (uint32_t)val64;
return UPB_STATUS_OK;
}
@ -277,20 +297,22 @@ void upb_parse_free(struct upb_parse_state *state)
free(state->stack);
}
static void pop_stack_frame(struct upb_parse_state *s)
static size_t pop_stack_frame(struct upb_parse_state *s)
{
if(s->submsg_end_cb) s->submsg_end_cb(s);
s->top--;
s->top = (struct upb_parse_stack_frame*)((char*)s->top - s->udata_size);
return s->top->end_offset;
}
static upb_status_t push_stack_frame(struct upb_parse_state *s, size_t end,
void *user_field_desc)
void *user_field_desc, size_t *end_offset)
{
s->top++;
s->top = (struct upb_parse_stack_frame*)((char*)s->top + s->udata_size);
if(s->top > s->limit) return UPB_ERROR_STACK_OVERFLOW;
s->top->end_offset = end;
*end_offset = end;
if(s->submsg_start_cb) s->submsg_start_cb(s, user_field_desc);
return UPB_STATUS_OK;
}
@ -298,7 +320,7 @@ static upb_status_t push_stack_frame(struct upb_parse_state *s, size_t end,
static upb_status_t parse_delimited(struct upb_parse_state *s,
struct upb_tag *tag,
void **buf, void *end,
size_t base_offset)
size_t base_offset, size_t *end_offset)
{
int32_t delim_len;
void *user_field_desc;
@ -318,7 +340,7 @@ static upb_status_t parse_delimited(struct upb_parse_state *s,
if(ft == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE) {
base_offset += ((char*)*buf - (char*)bufstart);
UPB_CHECK(push_stack_frame(s, base_offset + delim_len, user_field_desc));
UPB_CHECK(push_stack_frame(s, base_offset + delim_len, user_field_desc, end_offset));
} else {
void *delim_end = (char*)*buf + delim_len;
if(ft == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING ||
@ -337,7 +359,8 @@ static upb_status_t parse_delimited(struct upb_parse_state *s,
static upb_status_t parse_nondelimited(struct upb_parse_state *s,
struct upb_tag *tag,
void **buf, void *end)
void **buf, void *end,
size_t *end_offset)
{
/* Simple value or begin group. */
void *user_field_desc;
@ -346,7 +369,7 @@ static upb_status_t parse_nondelimited(struct upb_parse_state *s,
UPB_CHECK(skip_wire_value(buf, end, tag->wire_type));
} else if(ft == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP) {
/* No length specified, an "end group" tag will mark the end. */
UPB_CHECK(push_stack_frame(s, UINT32_MAX, user_field_desc));
UPB_CHECK(push_stack_frame(s, UINT32_MAX, user_field_desc, end_offset));
} else {
UPB_CHECK(s->value_cb(s, buf, end, user_field_desc));
}
@ -357,27 +380,29 @@ upb_status_t upb_parse(struct upb_parse_state *restrict s, void *buf, size_t len
size_t *restrict read)
{
void *end = (char*)buf + len;
*read = 0;
size_t offset = s->offset;
size_t end_offset = s->top->end_offset;
while(buf < end) {
struct upb_tag tag;
void *bufstart = buf;
UPB_CHECK(parse_tag(&buf, end, &tag));
if(tag.wire_type == UPB_WIRE_TYPE_END_GROUP) {
if(s->top->end_offset != UINT32_MAX)
if(end_offset != UINT32_MAX)
return UPB_ERROR_SPURIOUS_END_GROUP;
pop_stack_frame(s);
end_offset = pop_stack_frame(s);
} else if(tag.wire_type == UPB_WIRE_TYPE_DELIMITED) {
parse_delimited(s, &tag, &buf, end, s->offset + (char*)buf - (char*)bufstart);
UPB_CHECK(parse_delimited(
s, &tag, &buf, end, offset + (char*)buf - (char*)bufstart, &end_offset));
} else {
parse_nondelimited(s, &tag, &buf, end);
UPB_CHECK(parse_nondelimited(s, &tag, &buf, end, &end_offset));
}
size_t bytes_read = ((char*)buf - (char*)bufstart);
*read += bytes_read;
s->offset += bytes_read;
while(s->offset >= s->top->end_offset) {
if(s->offset != s->top->end_offset) return UPB_ERROR_BAD_SUBMESSAGE_END;
pop_stack_frame(s);
offset += ((char*)buf - (char*)bufstart);
while(offset >= end_offset) {
if(offset != end_offset) return UPB_ERROR_BAD_SUBMESSAGE_END;
end_offset = pop_stack_frame(s);
}
}
*read = offset - s->offset;
s->offset = offset;
return UPB_STATUS_OK;
}

Loading…
Cancel
Save