(micro)benchmarking Vimscript closures

Daniel Díaz Carrete
2 min readMar 2, 2020

Note to future readers: at the moment of writing this post, a new, faster version of Vimscript is in the works. This post is about the older pre-Vim9 version.

Did you know Vimscript supports closures? It does, see :h func-closure for the details. For example:

function Func1(x)
function! Func1Clos(y) closure
return a:x + a:y
endfunction
return funcref('Func1Clos')
endfunction

But there’s something I find gross about how they work. After Func1 is first executed, Func1Clos will be defined in the global scope as well! And you need to declare Func1Clos with function! because it seems to be redefined every time Func1 is invoked. As I said, gross.

One alternative is to use lambdas (see :h lamdba ) which are anonymous:

function Func2(x)
return { y -> a:x + y }
endfunction

One problem with Vimscript lambdas is that they must return an expression. If you want complex logic that requires defining new variables or invoking statements, you are out of luck.

A third solution is to delegate to a partially applied — see the {arglist} parameter in :h funcref() — auxiliary named function:

function Func3(x)
return funcref('Func3Clos',[a:x])
endfunction
function Func3Clos(x,y)
return a:x + a:y
endfunction

This solution at least makes the scope of the auxiliary function clearer.

I’m planning to write Vimscript code that will create a whole lot of closures, so I’m worried about potential performance differences between the three approaches. I wrote this — very naive! — microbenchmark:

function TestFunc(f,rounds)
let i = 0
let time1 = reltime()
while l:i < a:rounds
call a:f(1)(2)
let l:i+=1
endwhile
return reltime(l:time1)
endfunction

So, what are the results?

:echo reltimestr(TestFunc(funcref('Func1'),100000))
1.643
:echo reltimestr(TestFunc(funcref('Func2'),100000))
1.387
:echo reltimestr(TestFunc(funcref('Func3'),100000))
1.326

Lambdas are faster than named function closures, and partially applied auxiliary functions are a bit faster than lambdas.

Edit. it seems that the cost of parsing might play a part. The documentation for Vim9 says this about old-style Vimscript:

Execution is quite slow, every line is parsed every time it is executed.

--

--