Implementing strcmp, strlen, and strstr using SSE 4.2 instructions (2008)
strchr.com
strchr.com
> The following strcmp and strlen functions were written and published when the processors with SSE 4.2 support were not available yet. Later they were tested on real hardware and found to be correct.
Too many programmers often think of programming as an online operation - online with stackoverflow, the manual, npm install, and so on -- and in a world of always-online it's hard to justify "wasting" precious mental-space preserving the ability to program in a wifi-free zone like an airplane or on the underground, but you need the ability to think about your software to program on the chip that doesn't exist yet, so this may be interesting to meditate over from time to time.
https://software.intel.com/en-us/articles/intel-software-dev...
> Intel is releasing this Intel SDE so that developers can gain familiarity with our upcoming instruction set extensions.
if((my_bool)memcmp(password, hash) == 0)
Except that the SSE function would return nonzero of > 256 which would truncate to 0. Since the hash was seeded with time you could just spin-lock your way to entry on any system of that arch.So while they may have been "correct" there's always fun implications when you deploy them in the wild.
For short needle + haystack that fits in a vector reg, use SSE/AVX2 string search.
For short needle and long haystack, use SSE to find the first byte of the needle jumping a whole vector-register width at a time, then compare. If that produces too many false positives, fall back to SSE/AVX2 search for the whole needle.
For long needle, use a Rabin-Karp rolling hash to find a likely match, then verify.
The string search optimization rabbit hole can go pretty deep, especially because different implementations might win for different values passed to the function. Like, I'm guessing Go tries that search for just the first byte because, empirically, it helps pretty often, but it's not inherently faster for every possible needle and haystack (think needle ab haystack aaaaaaaaaaaaaa...ab), hence the escape hatch after too many false matches.
It's kind of wild what clever machinery gets invoked when you try to do simple things.
and here: http://0x80.pl/articles/simd-friendly-karp-rabin.html
Only sort of related to these links, but one thing I know very little about is whether you can use tricks like these to speed up related search-y ops that aren't exactly strstr or strchr. For example, finding an occurrence of any of a set of strings, or chars needing escaping for JSON or HTML/XML output. Of course, makes sense that most of the work goes to the most used fundamental functions; I'm just wondering out loud.
For multiple string match I was looking at doing Aho-Corasick for long strings in a SIMD-friendly way, but it's more a matter of an FSM with relatively few branch points, so that SIMD can digest long label matches quickly just as it does with long strcmp's.
Another set of benchmarks comparing simple strcmp to SSE 4.2: https://www.linkedin.com/pulse/simple-fast-string-comparison...
Other features of SSE 4.2
The strings do not need to be aligned.
The processor properly handles end-of-the-string case for zero-terminated strings and Pascal-style strings.
You can use the instructions with Unicode characters, signed or unsigned bytes.
Four aggregation operations can be used to implement a wide range of string-processing functions.
So presumably not, looks like the processor handles the end-of-string stuff natively.I also found this SO question which claims that trivial testcases break these implementations https://stackoverflow.com/questions/21714827/movdqu-instruct...
This code is just horrendously broken. Why has nobody else noticed it?
I did a quick skim (disclaimer it's sometimes hard to rule out that an SSE optimized version is in there somewhere).
* musl 1.1.16: no
* glibc 2.26: yes (also an avx2 optimized one)
* Apple Libc 1158.50.2: optimized (non-SSE2) versions of several str* funcs exist but strcpy() has an >=SSE impl
<time title="21 Dec 2008, 18:19" datetime="2008-12-21T18:19:19+06:00">9 years ago</time>It's supposed to be a comparison, but it doesn't set the flags with comparison results. Instead it uses them to signal if it's found an unequal byte, or hit the end of the strings. The latter is equality, which is one condition we want, but it doesn't say where in the 16 bytes it found the terminator. In fact it sets the register (ecx) which otherwise would be the offset to compare bytes, to 16.
That's all fine if you just want to return string comparison, but if you're trying to find their longest common prefix it's a whole new comparison to find the 0x00's.
The newer SIMD stuff seems to be factoring it out into bytewise compare and CLZ on a mask.