Success! Find is slow no more

Andrei Alexandrescu SeeWebsiteForEmail at erdani.com
Thu May 23 13:42:45 UTC 2019


A few comments within the code:

> T[] faster_find(T)(T[] haystack, T[] needle)
> {
>      if (needle.length == 0) return haystack[$..$];

This line should return haystack[0 .. 0] (an empty string matches at the 
beginning). Distinction is mainly academic but it will reduce the size 
of the function.

>      if (needle.length > haystack.length) return haystack[$..$];

I think this falls off naturally so no need to treat it as a special case.

(A good case to treat as a special case is needle.length == 1.)

>      immutable end = needle.length - 1;
>      size_t j = end, skip = 0;
> 
>      while(++j < haystack.length)

Whenever I see ++something I think "the first increment here is 
useless". By the same token it could happen that with something++ the 
last increment is useless. The difference is that the latter has fewer 
dependencies when it's also part of a comparison. I suggest looking into 
replacing ++j with starting at needle.length and doing the loop slightly 
differently.

>      {
>          if (haystack[j] != needle[end]) continue;

Inside this loop you have already checked the bounds so if the compiler 
generates them you may want to use .ptr[index]. There's some @trusted 
gymnastics you need to add.

>          for(size_t i = 1, k = 0; i < needle.length; ++i, ++k) {

Here you have two iteration variables that always differ by 1. You may 
want to use only one iteration variable. (I'm not sure this will improve 
matters for built-in types as there's enough registers.)

>              if (haystack[j - needle.length + i] != needle[k]) {
>                  while (skip < end && needle[end - 1 - skip] != 
> needle[end]) { ++skip; }
>                  j += skip;
>                  goto main_loop2;
>              }
>          }
>          return haystack[j - end .. $];
>      }
>      return haystack[$ .. $];
> 
>      main_loop2: while(++j < haystack.length)

Looks like the two versions of the loop differ only by the computation 
of skip. The code would be smaller (and therefore potentially faster) if 
skip were initialized e.g. to size_t.max and then initialized once. The 
test skip != size_t.max will fail only once and after that it will be 
reliably speculated.


More information about the Digitalmars-d mailing list