mirror of
https://github.com/neovim/neovim.git
synced 2025-09-29 06:28:35 +00:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)) {
|
||||
|
@@ -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
|
||||
|
@@ -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) \
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user