diff options
author | Lewis Russell <lewis6991@gmail.com> | 2022-07-27 23:35:01 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-27 23:35:01 +0100 |
commit | d27e4d657bf2c3181326f8c564c8684e80442100 (patch) | |
tree | b27c826841017899c8b13ddf80d2833aa229e335 | |
parent | 48608a1f46abfba130faa96d59bdf4b8283fe0ac (diff) | |
download | rneovim-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.c | 49 |
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); |