perf(ui): unpack a single ui event at a time, instead of a "redraw" batch

This reduces the memory overhead for large redraw batches, as a much smaller
prefix of the api object buffer is used and needs to be hot in cache.
This commit is contained in:
bfredl
2022-06-16 19:17:57 +02:00
parent 1b462705d0
commit 67a04fe6cb
5 changed files with 122 additions and 36 deletions

View File

@@ -239,7 +239,12 @@ static void parse_msgpack(Channel *channel)
{
Unpacker *p = channel->rpc.unpacker;
while (unpacker_advance(p)) {
if (p->type == kMessageTypeResponse) {
if (p->is_ui) {
if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
p->ui_handler.fn(p->result.data.array);
}
arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
} else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
if (p->request_id != frame->request_id) {
char buf[256];

View File

@@ -292,7 +292,18 @@ bool unpacker_advance(Unpacker *p)
if (!unpacker_parse_header(p)) {
return false;
}
p->state = p->type == kMessageTypeResponse ? 1 : 2;
if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) {
p->state = 10;
} else {
p->state = p->type == kMessageTypeResponse ? 1 : 2;
arena_start(&p->arena, &p->reuse_blk);
}
}
if (p->state == 10 || p->state == 11) {
if (!unpacker_parse_redraw(p)) {
return false;
}
arena_start(&p->arena, &p->reuse_blk);
}
@@ -310,13 +321,91 @@ rerun:
return false;
}
if (p->state == 1) {
switch (p->state) {
case 1:
p->error = p->result;
p->state = 2;
goto rerun;
} else {
assert(p->state == 2);
case 2:
p->state = 0;
p->is_ui = false;
return true;
case 12:
p->ncalls--;
if (p->ncalls > 0) {
p->state = 12;
} else if (p->nevents > 0) {
p->state = 11;
} else {
p->state = 0;
}
// TODO: fold into p->type
p->is_ui = true;
return true;
default:
abort();
}
}
// note: first {11} is not saved as a state
// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]]
//
bool unpacker_parse_redraw(Unpacker *p)
{
mpack_token_t tok;
int result;
const char *data = p->read_ptr;
size_t size = p->read_size;
switch (p->state) {
case 10:
result = mpack_rtoken(&data, &size, &tok);
if (result == MPACK_EOF) {
return false;
} else if (result || tok.type != MPACK_TOKEN_ARRAY) {
p->state = -1;
return false;
}
p->nevents = (int)tok.length;
FALLTHROUGH;
case 11:
result = mpack_rtoken(&data, &size, &tok);
if (result == MPACK_EOF) {
return false;
} else if (result || tok.type != MPACK_TOKEN_ARRAY) {
abort();
}
p->ncalls = (int)tok.length;
if (p->ncalls-- == 0) {
p->state = -1;
return false;
}
result = mpack_rtoken(&data, &size, &tok);
if (result == MPACK_EOF) {
return false;
} else if (result || (tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN)) {
abort();
}
if (tok.length > size) {
return false;
}
p->ui_handler = ui_client_get_redraw_handler(data, tok.length, NULL);
data += tok.length;
size -= tok.length;
p->nevents--;
p->read_ptr = data;
p->read_size = size;
p->state = 12;
return true;
default:
abort();
}
return true;
}

View File

@@ -32,8 +32,13 @@ struct Unpacker {
Error unpack_error;
Arena arena;
// one lenght free-list of reusable blocks
// one length free-list of reusable blocks
ArenaMem reuse_blk;
bool is_ui;
int nevents;
int ncalls;
UIClientHandler ui_handler;
};
// unrecovareble error. unpack_error should be set!