Wednesday, August 30, 2023

Re: Vim9 script performance vs. legacy



On Wed, Aug 30, 2023 at 5:25 PM Lifepillar <lifepillar@lifepillar.me> wrote:
I have rewritten Colortemplate v3¹, which is a somewhat complex project,
in Vim 9 script. This is how long it takes to build some sets of color
schemes on my laptop:

Template         | Legacy Vim | Vim 9 script
--------------------------------------------
Gruvbox 8        |     17.2s  |       3.7s
Solarized 8      |     17.4s  |       2.2s
Vim colorschemes²|     20.5s  |       3.2s

¹ https://github.com/lifepillar/vim-colortemplate
² https://github.com/vim/colorschemes

And this comparison is a bit apples vs oranges because it was not
a porting, but a complete rewrite. For instance, instead of an hoc
top-down recursive-descent parser, I am now using a parser combinators
library in functional style³—something that would be cumbersome to write
in legacy Vim script, and it would most likely perform horribly.
Functions calls are much more efficient in Vim 9 script.

This is really interesting; the fact that you have a real-life example instead of my contrived test makes your findings more valuable. About five to six times faster, notwithstanding your different implementation approach.
 

For a more apples-to-apples comparison, below is the execution time of
`libcolor.Neighbours()` from my libcolor library,³ which is a literal
translation of a legacy colortemplate#colorspace#k_neighbours() from
Colortemplate v2:

 k | Legacy Vim script | Vim 9 script
---|-------------------|-------------
 1 |          73.8 ms  |    5.1 ms
10 |         648.4 ms  |   42.9 ms
20 |        1164.1 ms  |   97.7 ms

The advantage of Vim 9 script is very significant. The benchmarking code
is at the end of this message for reference.

This is between 11 to 15+ times faster. Interestingly, running it 20 times reduced the advantage slightly. Just an anomaly where the CPU started doing something else, d'you suppose?
 

³ https://github.com/lifepillar/vim-devel

> I find that a significant amount of my script-writing time is used in
> calling functions such as matchlist, popup_create and the such, which seem
> to me to be identical (unless you tell me that Vim 9 has access to more
> optimised version of these).

I believe that built-in functions and commands should mostly show
similar performance.

I expect that, also; unless Vim 9 versions of these are written that are statically typed (which only matters in some cases where an argument could be one of several types), these aren't going to be the factors that determine the response time.
 

> Maybe the 'for' loop where I iterate over a list of strings is faster in
> Vim 9 because it's both compiled and statically typed?

Likely so. Not an answer to your question, but you may take a look at
some Vim 9 script benchmarks here:

    https://github.com/lacygoill/wiki/blob/main/vim/vim9.md

in the "What's the fastest between" section, and maybe compare with
similar loops in legacy Vim script.

Super useful link and pretty much answers a lot of the questions I had, including some I didn't manage to articulate!
 

From my experience on porting scripts to the new syntax, the stricter
typing rules help finding bugs and write cleaner code. It is a win,
regardless of speed.

I have actually ported a few of my existing scripts over to Vim 9, but I've tried to restrict it to more intensive tasks as legacy scripts are often fast enough for everyday work.
 

But Vim 9 script also has to coexist with the ecosystem of Vim
functions, and in some cases there is some unavoidable friction, as in
your example. For your specific example, I'd probably wrap the annoying
conversion into a function (did I say that function calls are cheap?):

def PopupLine(lineNumber: string): any
  if lineNumber == 'cursor'
    return lineNumber
  endif
  return str2nr(lineNumber)
enddef

Btw, differently from legacy script, an `if` command is not slower than
using `?`: both constructs gets compiled into the same bytecode.

I actually rewrote a routine with a ternary into an if/else earlier today for the sake of readability, but felt like I was damning it to a slower execution cycle while I did it. It's good to know that I might not have sacrificed performance for maintainability.

I have been trying to avoid 'any' as a type because I feel that it loses some of the speed advantages of Vim 9, but it can't always be helped. I had actually come up with version of your PopupLine that uses the fact that str2nr just returns 0 if the conversion fails as this allows more than just a value of 'cursor' (to make it a more generic function):

def GetStrOrNr(in: string): any
  if (in == '0')
    return 0
  endif

  var line: number = str2nr( in )

  return line == 0 ? in : line
enddef
 

> What do people use for their own stuff these days?

Only Vim 9 script. Bram gave us a wonderful gift, and I am very glad to
see that other developers are actively maintaining it and refining it.

I'm definitely using it for all my new stuff, also. Glad to be in good company.
 

Happy Vim scripting!
Life.

Thank you for the detailed response. Much appreciated.

All the best,

Salman

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/CANuxnEdUGNK0cpBARuamVLbs0b-kTGrW%3DL8kGLLv9GMQm8nBkw%40mail.gmail.com.

No comments: