refactor(arena): use a shared block freelist

This is both simpler in client code and more effective (always reuse
block hottest in cache)
This commit is contained in:
bfredl
2022-08-23 10:36:46 +02:00
parent c0d6052654
commit bcf5ee328e
15 changed files with 49 additions and 61 deletions

View File

@@ -114,8 +114,6 @@ static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
static char *e_invalwindow = N_("E957: Invalid window number");
static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
static ArenaMem eval_reuse_blk = NULL;
/// Dummy va_list for passing to vim_snprintf
///
/// Used because:
@@ -281,10 +279,6 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Error err = ERROR_INIT;
Arena res_arena = ARENA_EMPTY;
if (handler.arena_return) {
// TODO(bfredl): also use arena for vim_to_object
arena_start(&res_arena, &eval_reuse_blk);
}
Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) {
@@ -299,7 +293,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
end:
api_free_array(args);
if (handler.arena_return) {
arena_mem_free(arena_finish(&res_arena), &eval_reuse_blk);
arena_mem_free(arena_finish(&res_arena));
} else {
api_free_object(result);
}
@@ -6885,7 +6879,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
uint64_t chan_id = (uint64_t)argvars[0].vval.v_number;
const char *method = tv_get_string(&argvars[1]);
// TODO: putta in eval_reuse_blk
ArenaMem res_mem = NULL;
Object result = rpc_send_call(chan_id, method, args, &res_mem, &err);
@@ -6921,7 +6914,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
end:
arena_mem_free(res_mem, &eval_reuse_blk);
arena_mem_free(res_mem);
api_clear_error(&err);
}

View File

@@ -3154,7 +3154,6 @@ draw_cmdline_no_arabicshape:
static void ui_ext_cmdline_show(CmdlineInfo *line)
{
Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
Array content;
if (cmdline_star) {
content = arena_array(&arena, 1);
@@ -3199,7 +3198,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
line->special_shift,
line->level);
}
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
arena_mem_free(arena_finish(&arena));
}
void ui_ext_cmdline_block_append(size_t indent, const char *line)

View File

@@ -413,8 +413,6 @@ output:write([[
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
static ArenaMem lua_reuse_blk = { 0 };
]])
include_headers(output, headers)
output:write('\n')
@@ -496,7 +494,6 @@ local function process_function(fn)
cparams = cparams .. '&arena, '
write_shifted_output(output, [[
Arena arena = ARENA_EMPTY;
arena_start(&arena, &lua_reuse_blk);
]])
end
@@ -536,7 +533,7 @@ local function process_function(fn)
end
local free_retval
if fn.arena_return then
free_retval = "arena_mem_free(arena_finish(&arena), &lua_reuse_blk);"
free_retval = "arena_mem_free(arena_finish(&arena));"
else
free_retval = "api_free_"..return_type:lower().."(ret);"
end

View File

@@ -1303,7 +1303,7 @@ void free_highlight(void)
{
ga_clear(&highlight_ga);
map_destroy(cstr_t, int)(&highlight_unames);
arena_mem_free(arena_finish(&highlight_arena), NULL);
arena_mem_free(arena_finish(&highlight_arena));
}
#endif

View File

@@ -1093,7 +1093,7 @@ static int nlua_rpc(lua_State *lstate, bool request)
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, result, false);
arena_mem_free(res_mem, NULL);
arena_mem_free(res_mem);
}
} else {
if (!rpc_send_event(chan_id, name, args)) {

View File

@@ -60,6 +60,8 @@ void try_to_free_memory(void)
// Try to save all buffers and release as many blocks as possible
mf_release_all();
arena_free_reuse_blks();
trying_to_free = false;
}
@@ -528,23 +530,18 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
}
#define ARENA_BLOCK_SIZE 4096
#define REUSE_MAX 4
void arena_start(Arena *arena, ArenaMem *reuse_blk)
static struct consumed_blk *arena_reuse_blk;
static size_t arena_reuse_blk_count = 0;
static void arena_free_reuse_blks(void)
{
if (reuse_blk && *reuse_blk) {
arena->cur_blk = (char *)(*reuse_blk);
*reuse_blk = NULL;
arena->size = ARENA_BLOCK_SIZE;
arena->pos = 0;
// address is the same as as (struct consumed_blk *)arena->cur_blk
struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
assert((char *)blk == (char *)arena->cur_blk);
blk->prev = NULL;
} else {
arena->cur_blk = NULL;
arena->size = 0;
arena->pos = 0;
while (arena_reuse_blk_count > 0) {
struct consumed_blk *blk = arena_reuse_blk;
arena_reuse_blk = arena_reuse_blk->prev;
xfree(blk);
arena_reuse_blk_count--;
}
}
@@ -564,12 +561,18 @@ ArenaMem arena_finish(Arena *arena)
void alloc_block(Arena *arena)
{
struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
if (arena_reuse_blk_count > 0) {
arena->cur_blk = (char *)arena_reuse_blk;
arena_reuse_blk = arena_reuse_blk->prev;
arena_reuse_blk_count--;
} else {
arena_alloc_count++;
arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
}
arena->pos = 0;
arena->size = ARENA_BLOCK_SIZE;
struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
blk->prev = prev_blk;
arena_alloc_count++;
}
/// @param size if zero, will still return a non-null pointer, but not a unique one
@@ -606,15 +609,17 @@ void *arena_alloc(Arena *arena, size_t size, bool align)
return mem;
}
void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk)
void arena_mem_free(ArenaMem mem)
{
struct consumed_blk *b = mem;
// peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE,
// not a custom fix_blk
if (reuse_blk && *reuse_blk == NULL && b != NULL) {
*reuse_blk = b;
if (arena_reuse_blk_count < REUSE_MAX && b != NULL) {
struct consumed_blk *reuse_blk = b;
b = b->prev;
(*reuse_blk)->prev = NULL;
reuse_blk->prev = arena_reuse_blk;
arena_reuse_blk = reuse_blk;
arena_reuse_blk_count++;
}
while (b) {
@@ -815,6 +820,9 @@ void free_all_mem(void)
nlua_free_all_mem();
ui_free_all_mem();
// should be last, in case earlier free functions deallocates arenas
arena_free_reuse_blks();
}
#endif

View File

@@ -52,7 +52,7 @@ typedef struct {
size_t pos, size;
} Arena;
// inits an empty arena. use arena_start() to actually allocate space!
// inits an empty arena.
#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
#define kv_fixsize_arena(a, v, s) \

View File

@@ -158,7 +158,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
}
// frame.result was allocated in an arena
arena_mem_free(frame.result_mem, &rpc->unpacker->reuse_blk);
arena_mem_free(frame.result_mem);
frame.result_mem = NULL;
}
@@ -244,7 +244,7 @@ static void parse_msgpack(Channel *channel)
ui_client_event_raw_line(p->grid_line_event);
} else 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);
arena_mem_free(arena_finish(&p->arena));
}
} else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
@@ -295,7 +295,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
if (!p->handler.fn) {
send_error(channel, p->type, p->request_id, p->unpack_error.msg);
api_clear_error(&p->unpack_error);
arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
arena_mem_free(arena_finish(&p->arena));
return;
}
@@ -364,7 +364,7 @@ static void request_event(void **argv)
free_ret:
// e->args (and possibly result) are allocated in an arena
arena_mem_free(arena_finish(&e->used_mem), &channel->rpc.unpacker->reuse_blk);
arena_mem_free(arena_finish(&e->used_mem));
channel_decref(channel);
xfree(e);
api_clear_error(&error);

View File

@@ -181,13 +181,11 @@ void unpacker_init(Unpacker *p)
p->unpack_error = (Error)ERROR_INIT;
p->arena = (Arena)ARENA_EMPTY;
p->reuse_blk = NULL;
}
void unpacker_teardown(Unpacker *p)
{
arena_mem_free(p->reuse_blk, NULL);
arena_mem_free(arena_finish(&p->arena), NULL);
arena_mem_free(arena_finish(&p->arena));
}
bool unpacker_parse_header(Unpacker *p)
@@ -308,7 +306,7 @@ bool unpacker_advance(Unpacker *p)
p->state = 10;
} else {
p->state = p->type == kMessageTypeResponse ? 1 : 2;
arena_start(&p->arena, &p->reuse_blk);
p->arena = (Arena)ARENA_EMPTY;
}
}
@@ -322,7 +320,7 @@ bool unpacker_advance(Unpacker *p)
goto done;
} else {
// unpack other ui events using mpack_parse()
arena_start(&p->arena, &p->reuse_blk);
p->arena = (Arena)ARENA_EMPTY;
}
}
@@ -416,13 +414,13 @@ redo:
if (p->ui_handler.fn != ui_client_event_grid_line) {
p->state = 12;
if (p->grid_line_event) {
arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
arena_mem_free(arena_finish(&p->arena));
p->grid_line_event = NULL;
}
return true;
} else {
p->state = 13;
arena_start(&p->arena, &p->reuse_blk);
p->arena = (Arena)ARENA_EMPTY;
p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true);
g = p->grid_line_event;
}

View File

@@ -32,8 +32,6 @@ struct Unpacker {
Error unpack_error;
Arena arena;
// one length free-list of reusable blocks
ArenaMem reuse_blk;
int nevents;
int ncalls;

View File

@@ -156,7 +156,6 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_external) {
if (array_changed) {
Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
Array arr = arena_array(&arena, (size_t)size);
for (int i = 0; i < size; i++) {
Array item = arena_array(&arena, 4);
@@ -168,7 +167,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
pum_anchor_grid);
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
arena_mem_free(arena_finish(&arena));
} else {
ui_call_popupmenu_select(selected);
return;

View File

@@ -1094,7 +1094,6 @@ void draw_tabline(void)
static void ui_ext_tabline_update(void)
{
Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
size_t n_tabs = 0;
FOR_ALL_TABS(tp) {
@@ -1135,7 +1134,7 @@ static void ui_ext_tabline_update(void)
}
ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
arena_mem_free(arena_finish(&arena));
}
void get_trans_bufname(buf_T *buf)

View File

@@ -238,7 +238,7 @@ static void tinput_wait_enqueue(void **argv)
ArenaMem res_mem = NULL;
Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err);
consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
arena_mem_free(res_mem, NULL);
arena_mem_free(res_mem);
} else {
consumed = input_enqueue(keys);
}

View File

@@ -517,11 +517,10 @@ void ui_flush(void)
}
if (pending_mode_info_update) {
Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
Array style = mode_style_array(&arena);
bool enabled = (*p_guicursor != NUL);
ui_call_mode_info_set(enabled, style);
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
arena_mem_free(arena_finish(&arena));
pending_mode_info_update = false;
}
if (pending_mode_update && !starting) {

View File

@@ -47,8 +47,6 @@ enum {
typedef int LineFlags;
EXTERN ArenaMem ui_ext_fixblk INIT(= NULL);
struct ui_t {
bool rgb;
bool override; ///< Force highest-requested UI capabilities.