aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2022-07-27 23:35:01 +0100
committerGitHub <noreply@github.com>2022-07-27 23:35:01 +0100
commitd27e4d657bf2c3181326f8c564c8684e80442100 (patch)
treeb27c826841017899c8b13ddf80d2833aa229e335
parent48608a1f46abfba130faa96d59bdf4b8283fe0ac (diff)
downloadrneovim-d27e4d657bf2c3181326f8c564c8684e80442100.tar.gz
rneovim-d27e4d657bf2c3181326f8c564c8684e80442100.tar.bz2
rneovim-d27e4d657bf2c3181326f8c564c8684e80442100.zip
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
-rw-r--r--src/nvim/api/command.c49
1 files changed, 22 insertions, 27 deletions
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 56ca624232..96e643f1fe 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -306,7 +306,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
char *cmdline = NULL;
char *cmdname = NULL;
- char **args = NULL;
+ ArrayOf(String) args;
size_t argc = 0;
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");
}
- if (argc != 0) {
- 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);
- }
- }
+ args = cmd->args.data.array;
}
// 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.
- 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 (!(ea.argt & EX_RANGE)) {
@@ -676,10 +670,6 @@ end:
xfree(cmdname);
xfree(ea.args);
xfree(ea.arglens);
- for (size_t i = 0; i < argc; i++) {
- xfree(args[i]);
- }
- xfree(args);
return retv;
@@ -704,12 +694,11 @@ static bool string_iswhite(String str)
}
/// Build cmdline string for command, used by `nvim_cmd()`.
-///
-/// @return OK or FAIL.
-static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args,
- size_t argc)
+static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo,
+ ArrayOf(String) args, size_t argc)
{
StringBuilder cmdline = KV_INITIAL_VALUE;
+ kv_resize(cmdline, 32); // Make it big enough to handle most typical commands
// Add command modifiers
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.
size_t cmdname_idx = cmdline.size;
- kv_printf(cmdline, "%s", eap->cmd);
+ kv_concat(cmdline, eap->cmd);
// Command bang.
if (eap->argt & EX_BANG && eap->forceit) {
- kv_printf(cmdline, "!");
+ kv_concat(cmdline, "!");
}
// Command register.
@@ -791,29 +780,35 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
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->arglens = xcalloc(argc, sizeof(size_t));
+ size_t argstart_idx = cmdline.size;
for (size_t i = 0; i < argc; i++) {
- argidx[i] = cmdline.size + 1; // add 1 to account for the space.
- eap->arglens[i] = STRLEN(args[i]);
- kv_printf(cmdline, " %s", args[i]);
+ String s = args.items[i].data.string;
+ eap->arglens[i] = s.size;
+ 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
// values of eap->cmd, eap->arg and eap->args.
eap->cmd = cmdline.items + cmdname_idx;
eap->args = xcalloc(argc, sizeof(char *));
+ size_t offset = argstart_idx;
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.
- 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.
*cmdlinep = cmdline.items;
- xfree(argidx);
// Replace, :make and :grep with 'makeprg' and 'grepprg'.
char *p = replace_makeprg(eap, eap->arg, cmdlinep);