mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
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:
@@ -239,7 +239,12 @@ static void parse_msgpack(Channel *channel)
|
|||||||
{
|
{
|
||||||
Unpacker *p = channel->rpc.unpacker;
|
Unpacker *p = channel->rpc.unpacker;
|
||||||
while (unpacker_advance(p)) {
|
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);
|
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
|
||||||
if (p->request_id != frame->request_id) {
|
if (p->request_id != frame->request_id) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
@@ -292,9 +292,20 @@ bool unpacker_advance(Unpacker *p)
|
|||||||
if (!unpacker_parse_header(p)) {
|
if (!unpacker_parse_header(p)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) {
|
||||||
|
p->state = 10;
|
||||||
|
} else {
|
||||||
p->state = p->type == kMessageTypeResponse ? 1 : 2;
|
p->state = p->type == kMessageTypeResponse ? 1 : 2;
|
||||||
arena_start(&p->arena, &p->reuse_blk);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
@@ -310,13 +321,91 @@ rerun:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->state == 1) {
|
switch (p->state) {
|
||||||
|
case 1:
|
||||||
p->error = p->result;
|
p->error = p->result;
|
||||||
p->state = 2;
|
p->state = 2;
|
||||||
goto rerun;
|
goto rerun;
|
||||||
|
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 {
|
} else {
|
||||||
assert(p->state == 2);
|
|
||||||
p->state = 0;
|
p->state = 0;
|
||||||
}
|
}
|
||||||
|
// TODO: fold into p->type
|
||||||
|
p->is_ui = true;
|
||||||
return 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,8 +32,13 @@ struct Unpacker {
|
|||||||
Error unpack_error;
|
Error unpack_error;
|
||||||
|
|
||||||
Arena arena;
|
Arena arena;
|
||||||
// one lenght free-list of reusable blocks
|
// one length free-list of reusable blocks
|
||||||
ArenaMem reuse_blk;
|
ArenaMem reuse_blk;
|
||||||
|
|
||||||
|
bool is_ui;
|
||||||
|
int nevents;
|
||||||
|
int ncalls;
|
||||||
|
UIClientHandler ui_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
// unrecovareble error. unpack_error should be set!
|
// unrecovareble error. unpack_error should be set!
|
||||||
|
@@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid)
|
|||||||
if (ui_count == MAX_UI_COUNT) {
|
if (ui_count == MAX_UI_COUNT) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) {
|
if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]
|
||||||
|
&& !ui_client_channel_id) {
|
||||||
ui_comp_attach(ui);
|
ui_comp_attach(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,37 +46,23 @@ void ui_client_init(uint64_t chan)
|
|||||||
ui_client_channel_id = chan;
|
ui_client_channel_id = chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for "redraw" events sent by the NVIM server
|
UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error)
|
||||||
|
{
|
||||||
|
int hash = ui_client_handler_hash(name, name_len);
|
||||||
|
if (hash < 0) {
|
||||||
|
return (UIClientHandler){ NULL, NULL };
|
||||||
|
}
|
||||||
|
return event_handlers[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Placeholder for _sync_ requests with 'redraw' method name
|
||||||
///
|
///
|
||||||
/// This function will be called by handle_request (in msgpack_rpc/channel.c)
|
/// async 'redraw' events, which are expected when nvim acts as an ui client.
|
||||||
/// The individual ui_events sent by the server are individually handled
|
/// get handled in msgpack_rpc/unpacker.c and directy dispatched to handlers
|
||||||
/// by their respective handlers defined in ui_events_client.generated.h
|
/// of specific ui events, like ui_client_event_grid_resize and so on.
|
||||||
///
|
|
||||||
/// @note The "flush" event is called only once and only after handling all
|
|
||||||
/// the other events
|
|
||||||
/// @param channel_id: The id of the rpc channel
|
|
||||||
/// @param uidata: The dense array containing the ui_events sent by the server
|
|
||||||
/// @param[out] err Error details, if any
|
|
||||||
Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
|
Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < args.size; i++) {
|
api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request");
|
||||||
Array call = args.items[i].data.array;
|
|
||||||
String name = call.items[0].data.string;
|
|
||||||
|
|
||||||
int hash = ui_client_handler_hash(name.data, name.size);
|
|
||||||
if (hash < 0) {
|
|
||||||
ELOG("No ui client handler for %s", name.size ? name.data : "<empty>");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
UIClientHandler handler = event_handlers[hash];
|
|
||||||
|
|
||||||
// fprintf(stderr, "%s: %zu\n", name.data, call.size-1);
|
|
||||||
DLOG("Invoke ui client handler for %s", name.data);
|
|
||||||
for (size_t j = 1; j < call.size; j++) {
|
|
||||||
handler.fn(call.items[j].data.array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user