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_invalwindow = N_("E957: Invalid window number");
static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); 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 /// Dummy va_list for passing to vim_snprintf
/// ///
/// Used because: /// Used because:
@@ -281,10 +279,6 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Error err = ERROR_INIT; Error err = ERROR_INIT;
Arena res_arena = ARENA_EMPTY; 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); Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
@@ -299,7 +293,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
end: end:
api_free_array(args); api_free_array(args);
if (handler.arena_return) { if (handler.arena_return) {
arena_mem_free(arena_finish(&res_arena), &eval_reuse_blk); arena_mem_free(arena_finish(&res_arena));
} else { } else {
api_free_object(result); 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; uint64_t chan_id = (uint64_t)argvars[0].vval.v_number;
const char *method = tv_get_string(&argvars[1]); const char *method = tv_get_string(&argvars[1]);
// TODO: putta in eval_reuse_blk
ArenaMem res_mem = NULL; ArenaMem res_mem = NULL;
Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); 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: end:
arena_mem_free(res_mem, &eval_reuse_blk); arena_mem_free(res_mem);
api_clear_error(&err); api_clear_error(&err);
} }

View File

@@ -3154,7 +3154,6 @@ draw_cmdline_no_arabicshape:
static void ui_ext_cmdline_show(CmdlineInfo *line) static void ui_ext_cmdline_show(CmdlineInfo *line)
{ {
Arena arena = ARENA_EMPTY; Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
Array content; Array content;
if (cmdline_star) { if (cmdline_star) {
content = arena_array(&arena, 1); content = arena_array(&arena, 1);
@@ -3199,7 +3198,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
line->special_shift, line->special_shift,
line->level); 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) 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/lua/executor.h"
#include "nvim/memory.h" #include "nvim/memory.h"
static ArenaMem lua_reuse_blk = { 0 };
]]) ]])
include_headers(output, headers) include_headers(output, headers)
output:write('\n') output:write('\n')
@@ -496,7 +494,6 @@ local function process_function(fn)
cparams = cparams .. '&arena, ' cparams = cparams .. '&arena, '
write_shifted_output(output, [[ write_shifted_output(output, [[
Arena arena = ARENA_EMPTY; Arena arena = ARENA_EMPTY;
arena_start(&arena, &lua_reuse_blk);
]]) ]])
end end
@@ -536,7 +533,7 @@ local function process_function(fn)
end end
local free_retval local free_retval
if fn.arena_return then 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 else
free_retval = "api_free_"..return_type:lower().."(ret);" free_retval = "api_free_"..return_type:lower().."(ret);"
end end

View File

@@ -1303,7 +1303,7 @@ void free_highlight(void)
{ {
ga_clear(&highlight_ga); ga_clear(&highlight_ga);
map_destroy(cstr_t, int)(&highlight_unames); map_destroy(cstr_t, int)(&highlight_unames);
arena_mem_free(arena_finish(&highlight_arena), NULL); arena_mem_free(arena_finish(&highlight_arena));
} }
#endif #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); Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) { if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, result, false); nlua_push_Object(lstate, result, false);
arena_mem_free(res_mem, NULL); arena_mem_free(res_mem);
} }
} else { } else {
if (!rpc_send_event(chan_id, name, args)) { 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 // Try to save all buffers and release as many blocks as possible
mf_release_all(); mf_release_all();
arena_free_reuse_blks();
trying_to_free = false; 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 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) { while (arena_reuse_blk_count > 0) {
arena->cur_blk = (char *)(*reuse_blk); struct consumed_blk *blk = arena_reuse_blk;
*reuse_blk = NULL; arena_reuse_blk = arena_reuse_blk->prev;
arena->size = ARENA_BLOCK_SIZE; xfree(blk);
arena->pos = 0; arena_reuse_blk_count--;
// 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;
} }
} }
@@ -564,12 +561,18 @@ ArenaMem arena_finish(Arena *arena)
void alloc_block(Arena *arena) void alloc_block(Arena *arena)
{ {
struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk; 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->pos = 0;
arena->size = ARENA_BLOCK_SIZE; arena->size = ARENA_BLOCK_SIZE;
struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true); struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
blk->prev = prev_blk; blk->prev = prev_blk;
arena_alloc_count++;
} }
/// @param size if zero, will still return a non-null pointer, but not a unique one /// @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; return mem;
} }
void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk) void arena_mem_free(ArenaMem mem)
{ {
struct consumed_blk *b = mem; struct consumed_blk *b = mem;
// peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE, // peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE,
// not a custom fix_blk // not a custom fix_blk
if (reuse_blk && *reuse_blk == NULL && b != NULL) { if (arena_reuse_blk_count < REUSE_MAX && b != NULL) {
*reuse_blk = b; struct consumed_blk *reuse_blk = b;
b = b->prev; b = b->prev;
(*reuse_blk)->prev = NULL; reuse_blk->prev = arena_reuse_blk;
arena_reuse_blk = reuse_blk;
arena_reuse_blk_count++;
} }
while (b) { while (b) {
@@ -815,6 +820,9 @@ void free_all_mem(void)
nlua_free_all_mem(); nlua_free_all_mem();
ui_free_all_mem(); ui_free_all_mem();
// should be last, in case earlier free functions deallocates arenas
arena_free_reuse_blks();
} }
#endif #endif

View File

@@ -52,7 +52,7 @@ typedef struct {
size_t pos, size; size_t pos, size;
} Arena; } 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 ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
#define kv_fixsize_arena(a, v, s) \ #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 // 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; frame.result_mem = NULL;
} }
@@ -244,7 +244,7 @@ static void parse_msgpack(Channel *channel)
ui_client_event_raw_line(p->grid_line_event); ui_client_event_raw_line(p->grid_line_event);
} else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
p->ui_handler.fn(p->result.data.array); 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) { } else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); 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) { if (!p->handler.fn) {
send_error(channel, p->type, p->request_id, p->unpack_error.msg); send_error(channel, p->type, p->request_id, p->unpack_error.msg);
api_clear_error(&p->unpack_error); api_clear_error(&p->unpack_error);
arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); arena_mem_free(arena_finish(&p->arena));
return; return;
} }
@@ -364,7 +364,7 @@ static void request_event(void **argv)
free_ret: free_ret:
// e->args (and possibly result) are allocated in an arena // 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); channel_decref(channel);
xfree(e); xfree(e);
api_clear_error(&error); api_clear_error(&error);

View File

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

View File

@@ -32,8 +32,6 @@ struct Unpacker {
Error unpack_error; Error unpack_error;
Arena arena; Arena arena;
// one length free-list of reusable blocks
ArenaMem reuse_blk;
int nevents; int nevents;
int ncalls; 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 (pum_external) {
if (array_changed) { if (array_changed) {
Arena arena = ARENA_EMPTY; Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
Array arr = arena_array(&arena, (size_t)size); Array arr = arena_array(&arena, (size_t)size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Array item = arena_array(&arena, 4); 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, ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
pum_anchor_grid); pum_anchor_grid);
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); arena_mem_free(arena_finish(&arena));
} else { } else {
ui_call_popupmenu_select(selected); ui_call_popupmenu_select(selected);
return; return;

View File

@@ -1094,7 +1094,6 @@ void draw_tabline(void)
static void ui_ext_tabline_update(void) static void ui_ext_tabline_update(void)
{ {
Arena arena = ARENA_EMPTY; Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
size_t n_tabs = 0; size_t n_tabs = 0;
FOR_ALL_TABS(tp) { 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); 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) void get_trans_bufname(buf_T *buf)

View File

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

View File

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

View File

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