Fastest way to compute iterates of SHA1 It's well-known that computing lots of hash functions (like SHA1) in parallel involves the use of GPUs in most cases.  However, when doing something serially, such as computing sha1(sha1(sha1(...sha1(x)))) parallelism would not seem to be that useful.

I ran into this very problem recently, and I started running a program on my Mac laptop using python, at about 270k hashes/sec.  Moving to a C implementation, I was able to get 750k hashes/sec.  And strangely, using VirtualBox with Kali and the same C implementation, about 1.3m hashes/sec.

(There is some discussion one can find online regarding why a Linux version of SHA1 code might be faster than a Mac OS version, even when the former is through a virtualization layer, but this is beyond the scope of my question.)

Question: If I want to compute SHA1 as fast as possible and parallelism is not helpful, what's the fastest way to do it?  I suppose the answer involves special hardware?  Or buying a desktop with a good CPU and overclocking?
There is no known mathematical trick to compute iterates of SHA-1. Getting high throughput in software involves:


Use of parallelism if at all possible; that is, if starting from multiple points. Depending on why one computes iterates of SHA-1 that is possible, or not.
Specializing the code to compute the hash of a 20-byte message obtained as as hash, removing endianness conversion and padding in the main iteration. That is, to compute an $n$-iterate of a message per SHA-1:


hash the message per normal SHA-1;
convert the (bytestring) result to 5 native-endian 32-bit words, yielding the current iterate;
repeat $n-1$ times:


perform a single SHA-1 round, starting from the SHA-1 Initialization Vector 0x67452301 0xEFCDAB89 0x98BADCFE 0x10325476 0xC3D2E1F0, hashing the 16-word block consisting of the 5 words of the current iterate, 9 zero words, the word 0x000000A0 (for 160 bits), and a zero word; yielding the new value of the current iterate, manipulating 32-bit (or wider) quantities only;

convert the current iterate to (bytestring) result.

Coding and micro-optimizing carefully, perhaps in assembly. That a 73% throughput increase was achievable merely by changing compiler is telling that this counts. Among techniques likely to help:


Fully unroll the 80 rounds
Take advantage of constant words in IV and 16-word block hashed
(b&amp;c)|((~b)&amp;d) is ((b^c)&amp;d)^c and that might help (or not) for rounds 0..19
(b&amp;c)|(c&amp;d)|(d&amp;b) is ((d|b)&amp;c)|(d&amp;b) and typically helps for rounds 40..59
Schedule and choose what goes into what register wisely.



Update: on selected CPUs, there are special instructions; see this Intel whitepaper and reference code.



For the ultimate in speed, there's FPGA; an ASIC would be even faster, but wasteful.