(micro)benchmarking Vimscript closures
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])
endfunctionfunction 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.