mirror of
https://github.com/neovim/neovim.git
synced 2025-10-09 03:16:31 +00:00
perf(api): optimize nvim_cmd (#19513)
Reduce the amount of string allocations and length calculations. With the following benchmark: ```lua total = 0 for _ = 1, loops do local start = now() vim.api.nvim_cmd({cmd = 'let', args = {'a', '=', '1'}}, {}) total = total + (now() - start) end print('nvim_cmd', total / loops) ``` ``` hyperfine 'nvim --clean test.lua +source +q' ``` Before: 234.5ms After: 173.8ms
This commit is contained in:
@@ -306,7 +306,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
|||||||
|
|
||||||
char *cmdline = NULL;
|
char *cmdline = NULL;
|
||||||
char *cmdname = NULL;
|
char *cmdname = NULL;
|
||||||
char **args = NULL;
|
ArrayOf(String) args;
|
||||||
size_t argc = 0;
|
size_t argc = 0;
|
||||||
|
|
||||||
String retv = (String)STRING_INIT;
|
String retv = (String)STRING_INIT;
|
||||||
@@ -416,18 +416,12 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
|||||||
VALIDATION_ERROR("Incorrect number of arguments supplied");
|
VALIDATION_ERROR("Incorrect number of arguments supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc != 0) {
|
args = cmd->args.data.array;
|
||||||
args = xcalloc(argc, sizeof(char *));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < argc; i++) {
|
|
||||||
args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
|
// Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
|
||||||
// since it only ever checks the first argument.
|
// since it only ever checks the first argument.
|
||||||
set_cmd_addr_type(&ea, argc > 0 ? args[0] : NULL);
|
set_cmd_addr_type(&ea, argc > 0 ? args.items[0].data.string.data : NULL);
|
||||||
|
|
||||||
if (HAS_KEY(cmd->range)) {
|
if (HAS_KEY(cmd->range)) {
|
||||||
if (!(ea.argt & EX_RANGE)) {
|
if (!(ea.argt & EX_RANGE)) {
|
||||||
@@ -676,10 +670,6 @@ end:
|
|||||||
xfree(cmdname);
|
xfree(cmdname);
|
||||||
xfree(ea.args);
|
xfree(ea.args);
|
||||||
xfree(ea.arglens);
|
xfree(ea.arglens);
|
||||||
for (size_t i = 0; i < argc; i++) {
|
|
||||||
xfree(args[i]);
|
|
||||||
}
|
|
||||||
xfree(args);
|
|
||||||
|
|
||||||
return retv;
|
return retv;
|
||||||
|
|
||||||
@@ -704,12 +694,11 @@ static bool string_iswhite(String str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build cmdline string for command, used by `nvim_cmd()`.
|
/// Build cmdline string for command, used by `nvim_cmd()`.
|
||||||
///
|
static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo,
|
||||||
/// @return OK or FAIL.
|
ArrayOf(String) args, size_t argc)
|
||||||
static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args,
|
|
||||||
size_t argc)
|
|
||||||
{
|
{
|
||||||
StringBuilder cmdline = KV_INITIAL_VALUE;
|
StringBuilder cmdline = KV_INITIAL_VALUE;
|
||||||
|
kv_resize(cmdline, 32); // Make it big enough to handle most typical commands
|
||||||
|
|
||||||
// Add command modifiers
|
// Add command modifiers
|
||||||
if (cmdinfo->cmdmod.cmod_tab != 0) {
|
if (cmdinfo->cmdmod.cmod_tab != 0) {
|
||||||
@@ -779,11 +768,11 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
|
|||||||
|
|
||||||
// Keep the index of the position where command name starts, so eap->cmd can point to it.
|
// Keep the index of the position where command name starts, so eap->cmd can point to it.
|
||||||
size_t cmdname_idx = cmdline.size;
|
size_t cmdname_idx = cmdline.size;
|
||||||
kv_printf(cmdline, "%s", eap->cmd);
|
kv_concat(cmdline, eap->cmd);
|
||||||
|
|
||||||
// Command bang.
|
// Command bang.
|
||||||
if (eap->argt & EX_BANG && eap->forceit) {
|
if (eap->argt & EX_BANG && eap->forceit) {
|
||||||
kv_printf(cmdline, "!");
|
kv_concat(cmdline, "!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command register.
|
// Command register.
|
||||||
@@ -791,29 +780,35 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
|
|||||||
kv_printf(cmdline, " %c", eap->regname);
|
kv_printf(cmdline, " %c", eap->regname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through each argument and store the starting index and length of each argument
|
|
||||||
size_t *argidx = xcalloc(argc, sizeof(size_t));
|
|
||||||
eap->argc = argc;
|
eap->argc = argc;
|
||||||
eap->arglens = xcalloc(argc, sizeof(size_t));
|
eap->arglens = xcalloc(argc, sizeof(size_t));
|
||||||
|
size_t argstart_idx = cmdline.size;
|
||||||
for (size_t i = 0; i < argc; i++) {
|
for (size_t i = 0; i < argc; i++) {
|
||||||
argidx[i] = cmdline.size + 1; // add 1 to account for the space.
|
String s = args.items[i].data.string;
|
||||||
eap->arglens[i] = STRLEN(args[i]);
|
eap->arglens[i] = s.size;
|
||||||
kv_printf(cmdline, " %s", args[i]);
|
kv_concat(cmdline, " ");
|
||||||
|
kv_concat_len(cmdline, s.data, s.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Done appending to cmdline, ensure it is NUL terminated
|
||||||
|
kv_push(cmdline, NUL);
|
||||||
|
|
||||||
// Now that all the arguments are appended, use the command index and argument indices to set the
|
// Now that all the arguments are appended, use the command index and argument indices to set the
|
||||||
// values of eap->cmd, eap->arg and eap->args.
|
// values of eap->cmd, eap->arg and eap->args.
|
||||||
eap->cmd = cmdline.items + cmdname_idx;
|
eap->cmd = cmdline.items + cmdname_idx;
|
||||||
eap->args = xcalloc(argc, sizeof(char *));
|
eap->args = xcalloc(argc, sizeof(char *));
|
||||||
|
size_t offset = argstart_idx;
|
||||||
for (size_t i = 0; i < argc; i++) {
|
for (size_t i = 0; i < argc; i++) {
|
||||||
eap->args[i] = cmdline.items + argidx[i];
|
offset++; // Account for space
|
||||||
|
eap->args[i] = cmdline.items + offset;
|
||||||
|
offset += eap->arglens[i];
|
||||||
}
|
}
|
||||||
// If there isn't an argument, make eap->arg point to end of cmdline.
|
// If there isn't an argument, make eap->arg point to end of cmdline.
|
||||||
eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size;
|
eap->arg = argc > 0 ? eap->args[0] :
|
||||||
|
cmdline.items + cmdline.size - 1; // Subtract 1 to account for NUL
|
||||||
|
|
||||||
// Finally, make cmdlinep point to the cmdline string.
|
// Finally, make cmdlinep point to the cmdline string.
|
||||||
*cmdlinep = cmdline.items;
|
*cmdlinep = cmdline.items;
|
||||||
xfree(argidx);
|
|
||||||
|
|
||||||
// Replace, :make and :grep with 'makeprg' and 'grepprg'.
|
// Replace, :make and :grep with 'makeprg' and 'grepprg'.
|
||||||
char *p = replace_makeprg(eap, eap->arg, cmdlinep);
|
char *p = replace_makeprg(eap, eap->arg, cmdlinep);
|
||||||
|
Reference in New Issue
Block a user