2

As far as I know I have these two options to compare a string with a pattern:

  • Using the =~ comparison.
  • Using the matchstr() function.

So it looks like this:

" with =~
if string =~ "pattern"
    <do something>

" with matchstr()
elseif matchstr(string, "pattern") != ''
    <do something>
endif

I'm wondering which version of them is the fastest (or is there even a third possibility which is even faster?).

2 Answers 2

7

The operator version =~ is most likely faster than matchstr. This is for a couple reasons:

  • function calls in vim script have a cost
  • matchstr returns a string, therefore has an extra allocation

I've done a direct performance comparison.

let t = reltime()
for x in range(1000000)
    if matchstr('strpatingpattern', 'pattern')
    endif
endfor
echo reltimestr(reltime(t))

let t = reltime()
for x in range(1000000)
    if match('strpatingpattern', 'pattern')
    endif
endfor
echo reltimestr(reltime(t))

let t = reltime()
for x in range(1000000)
    if 'strpatingpattern' =~ 'pattern'
    endif
endfor
echo reltimestr(reltime(t))

Results:

  3.992500
  3.817862
  3.354163

The differences are marginal, but we can see matchstr is the slowest approach, match in the middle, and =~ is the fastest.

Use =~ when you need a binary output, use match or matchstr when you need the extra outputs or inputs provided.

1
  • I’d be interested in seeing =~#, too :)
    – D. Ben Knoble
    Feb 3, 2021 at 12:43
5

I've run @Mass bench against another one that avoids :for as I have the experience of extra costs associated to control statements.

My convoluted solution also does its best to avoid lambdas.

function! lh#time#bench_n2(n, F, ...) abort
  if type(a:F) == type(function('type'))
    let benchs = map(range(1, a:n), '[reltime()] + [a:F()] + [reltime()]')
  else
    let benchs = map(range(1, a:n), '[reltime()] + ['.a:F.'] + [reltime()]')
  endif
  let res = benchs[0][1]
  call map(benchs, 'reltimefloat(reltime(v:val[0], v:val[2]))')
  let accu = [0]
  call map(copy(benchs), 'add(accu, v:val + accu[-1])')
  return [res, accu[-1]]
endfunction

With the following on top of Mass' bench

echo lh#time#bench_n2(1000000, "matchstr('strpatingpattern', 'pattern') ")
echo lh#time#bench_n2(1000000, "match('strpatingpattern', 'pattern') ")
echo lh#time#bench_n2(1000000, "'strpatingpattern' =~ 'pattern' ")
echo lh#time#bench_n2(1000000, "'strpatingpattern' =~# 'pattern' ")

In the end, the results are a little bit more stable on my machine -- yet sometimes =~ is a little bit faster than =~#, sometimes it's the other way around. And also, now I'm sure it's convoluted and not necessary: Mass' simple solution is more than enough.

" Mass' bench in order to have an a idea 
  5.726717
  5.520035
  4.839088

" Mine
['pattern', 4.724165]
[9, 4.75635]
[1, 4.313803]
[1, 4.557732]

In conclusion, yeah matchstr() is a little bit slower than match() wich is a little bit slower than =~, but honestly? I doubt you'll ever notice the difference, even when processing 100k or 1M tags. When you need matchstr(), or match() or ... extra services, use them. Otherwise use whatever you wish/prefer/like to test whether it matches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.