Wednesday, August 30, 2023

Re: Vim9 script performance vs. legacy

On 2023-08-29, Salman Halim <salmanhalim@gmail.com> wrote:
> Hello,
>
> I've been writing my scripts using Vim 9 recently (though without classes)
> and was wondering if anybody had any performance metrics/benchmarks they
> would be willing to share that compare Vim 9's speed compared to the same
> thing written in legacy code.

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.

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.

³ 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.

> 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.

> One of the things that caught me recently was that popup_create takes a
> parameter called 'line' which can be either a number or the word 'cursor'.
> It cannot be the STRING '2', it has to be the NUMBER 2 while 'cursor' is a
> string. In legacy script, no problem. Here, I had to make it a string
> variable and then see do something like 'lineNumber != "cursor" ?
> str2nr(lineNumber) : lineNumber' to convert it to a number if needed. i'm
> trying to figure out whether there is a measurable speed advantage to going
> through these motions or if I should just write my scripts in legacy Vim
> script.

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.

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.

> 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.

Happy Vim scripting!
Life.

---
vim9script

# Benchmarking a naive k-neighbors algorithm
# in legacy Vim script vs Vim 9 script

import 'libcolor.vim' as libcolor

def Benchmark(Fn: func, args: list<any> = [], repeat = 1): float
var i = 0
const start = reltime()

while i < repeat
call(Fn, args)
++i
endwhile

return 1000 * reltime(start)->reltimefloat() / repeat
enddef

const n_repeat = 10

for k in [1, 10, 20]
# Vim 9 script
echomsg Benchmark(libcolor.Neighbours, ['#f54f29', k], n_repeat) "ms"

# Legacy Vim script
echomsg Benchmark(
colortemplate#colorspace#k_neighbours, ['#f54f29', k], n_repeat
) "ms"
endfor


--
--
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/ucoc3n%24od6%241%40ciao.gmane.io.

No comments: