The search for a faster CRC32
blog.fastmail.com
blog.fastmail.com
In this case, I think (but am curious, will investigate further at some point) our Cyrus servers are doing enough checksumming work to keep any tables hot in the cache. So the tests are hopefully a useful indicator of where improvements can be made.
Also, our oldest CPU on production servers seems to be the E5520 right now, which has 128kb of L1 data cache.
- Instruction Cache = 32kB, per core
- Data Cache = 32kB, per core
- 256kB Mid-Level cache, per core
- 8MB shared among cores (up to 4)
So I guess the confusion is that Intel moved the L2 cache onto each core (from Nehalem onwards, I think?) and used that opportunity to substantially lower latency for it.[1] http://www.intel.com/content/www/us/en/processors/xeon/xeon-...
Why on earth is this a showstopper?
Also, literature suggests a throughput of ∼2.67 bytes per cycle for the CRC32 instruction, a three fold improvement over best in class non-HW routines. I'm quite sure it would be worth it to reconvert previous checksums if you can do so in a way that minimizes downtime (think TrueCrypt doing a transparent initial encryption; not encrypting when theres IO load).
As an aside, if they have a server in production that doesn't support CLMUL, they should junk that machine - it appeared in Westmere and Bulldozer - everything earlier is EOLed. Crc32 was in sse4.2, so Nehalem.
The crc32 instruction is hard to beat, has been around for years, and I would guess the different poly could be phased in on their systems like you would for changing a password algorithm, or just try the new poly since it is quick, and if it fails use the old poly.
A parallel CRC32 can be slightly faster with more data segment used, and more code size.
Also PCLMULQDQ can use an polynomial I think. So they could make a compatible version.
The lookup table version they used has to have used lots more CPU cache. Also, I would suggest they look at reducing the amount of times they call the function. Since there are two ways to speed up functions that are called a lot. One is to stop calling it so many times.
Or since IO is limiting, they could consider compressing/decompressing the data as well. Using something fast like LZ4 compression. This will give them faster IO and a checksum at the same time. Something like Blosc can be faster than memcpy. Especially since the data is replicated over the network as well, and they would save space on their storage.
Modern CPU performance optimization is very often about memory IO. If the data is in L2/L3 then you can do a LOT of computation on it, almost for free, compared to the time it takes to get it into L2/L3.
During that time waiting for memory/disk/network IO, they might consider other things. Like indexing, better checksums (like SHA1) or even encryption. Especially if the data is already in L2/L3.
Why didn't they extract the CRC32 code from the kernel and try it directly?
https://github.com/torvalds/linux/blob/master/lib/crc32.c https://github.com/torvalds/linux/blob/master/include/linux/...
[Intel's CRC32 CPU instructions] uses different inputs to the CRC32 algorithm (known as the polynomial) which is apparently more robust, and is used in networks, filesystems, that sort of thing. It gives different results to the "standard" polynomial, typically used in compression.
They would have to go back and recompute all their existing stored checksums.
Replacing a function with a faster implementation is trivial; its just another deployment and we do several of those each week. Changing data format adds operational complexity - two codepaths need to be run and maintained and we increase system load and lower response times for the duration. (The data format is versioned, so that's easy - no architectural problem there).
Its totally worth doing if you need to, and we're not afraid of that, but you don't do it on a whim - it takes proper planning and testing and needs multiple people involved.
Wait, so are these results without letting the compiler optimize?
The exception is the Debian packaged version of zlib, because we don't control that. That's the reason I include stock zlib in the tests - if it had been wildly different from the system zlib, I would have looked into recompiling the Debian package with more optimisations. There were no major differences and indeed, inspecting the package further shows that it is compiled with optimisations.
On the Cyrus side, we used to link to the Debian zlib, so we get those optimisations. For the new code bundled in Cyrus itself, we have to enable optimisations ourselves.
"Easy to rewrite in other languages (e.g. Perl!)"
[1] http://opera.brong.fastmail.fm.user.fm/talks/twoskip/twoskip...
"Because it produces different results, it would render all our stored checksums invalid, so we'd have to recalculate them all. That's a big job, so we'd need to do a lot of testing first to make sure that it's so much better that it's actually worth the effort."
http://fastcompression.blogspot.fr/2012/04/selecting-checksu...
http://fastcompression.blogspot.com/2013/12/finite-state-ent...
http://www2.imm.dtu.dk/projects/thrash-workshop/slides/thoru...
https://en.wikipedia.org/wiki/Tabulation_hashing
I use it all the time when I need a hash table index. It's simple to understand, safe and fast. Even though it can generate more assembly than other hashes, it's actually often faster because it has no multiply operation, only XOR. If the tables are small enough to fit into cache (i.e. small keys), it's brilliant. It has properties suitable for triangular probing, where other hashes sometimes don't have enough independence. And if you need a rolling hash, i.e. for Rabin Karp, it's also perfect.
As an example from one of my hash function research projects, MetroHash64 will outperform xxhash both for speed and stastical robustness (which is quasi-cryptographic). If you only need 32 bits, truncate larger hashes; if they have very strong stastical properties, that works well.
Lots of people working on this stuff.
You are discounting xxHash considerably. xxHash is one of the best, if not the best, all factors considered.
SMHasher:
xxHash, speed=5.4 GB/s, quality=10
MurmurHash 3a, speed=2.7 GB/s, quality=10
SBox, speed=1.4 GB/s, quality=9
Lookup3, speed=1.2 GB/s, quality=9
CityHash64, speed=1.05 GB/s, quality=10
FNV, speed=0.55 GB/s, quality=5
CRC32, speed=0.43 GB/s, quality=9
MD5-32, speed=0.33 GB/s, quality=10
SHA1-32, speed=0.28 GB/s, quality=10Getting a perfect score with SMHasher is table stakes. The default settings on SMHasher are much too loose for current research; I know it well, I have hacked it extensively. A state-of-the-art non-cryptographic hash function today generally has the following properties:
- statistical quality greater than or equal to MD5, a cryptographic hash. xxHash is not close to MD5 in quality.
- faster than xxHash on large keys, and for really good hashes, memory bandwidth bound. (Metro64 is, again, faster than xxHash, though not memory bandwidth bound.)
- faster than xxHash on small keys (Metro64, to use that example, is almost 2x faster)
There are a lot of good hash functions being developed by researchers. But empirically, xxHash is nowhere near the state-of-the-art any way you slice it. It is a decent hash function but there are many functions produced by many researchers that are both faster and higher quality. It isn't personal, people can measure it for themselves.
http://www.jandrewrogers.com/2015/05/27/metrohash/
Interesting that you based your results there on SMHasher. Are there better benchmarks available?
xxHash is one of the best in terms of speed and quality.