Show HN: Accelerating SHA256 by 100x in Golang on ARM
blog.minio.io
blog.minio.io
In addition, when there is dedicated hardware, it is also significantly easier to add a SHA instruction than attempt to recognise sequences of many regular instructions that could be "fused together" into a single operation using that hardware. When there isn't, adding such an instruction that gets internally expanded into multiple uops for the equivalent using the existing functional units is still beneficial, since it leaves open room for an immediate performance gain on all existing software when a future revision does add that dedicated hardware, or optimises its microarchitecture to allow those (possibly different) uops to execute faster.
Despite the name, ARM is definitely not very RISC anymore, and that's what has kept it competitive.
If and when speed matters, ASIC beats a general-purpose CPU hands down, every time.
[1] http://www.xilinx.com/products/silicon-devices/soc.html
[2] http://www.pcworld.com/article/3055526/intel-starts-baking-s...
For a counterexample, see the x86 string instructions. "Hardware" implementations like `repne scasb` are routinely outperformed by software implementations using SSE2.
Another problem is that these instructions don't die. SHA will some day be replaced, but the instruction will live on. The x86 BCD instructions illustrate this.
From what I remember, RISC is less about "simple" instructions, and more about regular instructions which can execute with predictable throughput, ideally one per clock cycle. "Do this particular arithmetic" is in the RISC philosophy, while looping instructions (rep, lswi, etc.) are not.
That's only because SCAS (and CMPS) has not (yet) received quite the same amount of attention as MOVS and STOS. For a counterexample to your counterexample, look up "enhanced REP MOVSB". REP MOVS/STOS can operation on cacheline-sized blocks since at least the P6, when it was introduced as the "fast strings" feature, and its performance has been steadily improved over the processor generations.
Another problem is that these instructions don't die. SHA will some day be replaced, but the instruction will live on. The x86 BCD instructions illustrate this.
Replaced for secure crypto, yes, but there are plenty of other applications like (nonmalicious) data corruption detection where a reasonably fast yet far more collision-resistant algorithm than regular CRC is very useful.
On the topic of BCD instructions, there's this: https://news.ycombinator.com/item?id=8477254
Intel Goldmont is the only microarch that implements the instructions. It was "released" in April: http://www.extremetech.com/computing/226800-intels-new-low-c... But it is one of these annoying soft launches where the processors are not for sale anywhere, the specs are incomplete, and even ark.intel.com doesn't know about them. sigh
Should something be understood instead?
And now Intel re-introduce the SHA instructions in some low-power low-end CPUs, but not for any desktop or single-socket server CPU? What a bizarre case of feature fragmentation. Typical Intel.
As far as I know there are no SHA extensions before Goldmont, as mrb said, and in Cannonlake for non-low-power chips.
Still a worthwhile article but the title seemed to make me think of implementation/algorithm changes.
Insertionsort, Mergesort and Timsort are three wildly different algorithms with different speeds, but on every possible input they produce the exact same result
(I'm unclear if this performance oddity remains true with the crypto hardware extensions being used here.)
Intel seems to be the same way [2](2013) - i.e. looping over multiple instructions per 64 byte block.
[1] https://github.com/minio/sha256-simd/blob/master/sha256block... [2] https://software.intel.com/en-us/articles/intel-sha-extensio...
This code is such a good example of the potential value of formal verification tools for crypto primitives.
I would expect SHA512 to be 2x faster compared to the SHA256 software version, but that is still way slower than the ARM SHA extensions accelerated version.
https://github.com/minio/sha256-simd#comparison-to-other-has...
I'm not sure why this should be a problem, it just shows attention to performance in my opinion.
Either you keep adding intrisics to the compiler, or outsource it to an external Assembler.
Even managed languages have bytecode Assemblers available.
Optimized C libraries like compressors or security have assembly implementations as well. I'm not sure why using assembler should be considered a signal of a defect rather than a proof of optimization. There's really nothing wrong in using assembly to write a bytes.Index version optimized for AVX2.
Well, actually, they could, it's just not worth pattern matching because it occurs so infrequently.
"but history has shown that things like autovectorization are too fragile and can't be relied upon."
Errr, i'd say the opposite. History has shown that good autovectorizing compilers can come pretty damn close to whatever you want.
Usually the only issue is that they may insert too many runtime checks, and that's easily solvable.
The large majority of performance-sensitive libraries I know of rely on carefully written native code (C, C++), with a mixture of assembly and/or intrinsics. This, to me, is a failure to concretize autovectorization from textbooks into an industry-accepted solution.
Intrinsics.
There's basically zero benefit to coding this stuff in assembly. Many standard libraries prefer intrinsics, because there's basically zero benefit to writing this stuff in assembly and many drawbacks from an optimization perspective.
Also I'd like to know what you would consider an appropriate amount of assembly code.
Back when compilers were sold, the professional version always had an assembler in the box.
In MS-DOS even BASIC compilers like Turbo Basic could use inline Assembly.
Why is the code written as words like this instead of just the assembly instructions?
That sort of thing is quite common in Go assembler. The assemblers are a little primitive and they force the assembly language for each processor into a common pattern, so when you are writing ARM assembler for instance, everything is backwards.
I imagine having rationalised the assemblers between processors somewhat it makes the core developers lives easier though who have to lightly touch lots of different processor assembler.
Because for me it feels very natural, given the Z80 and Intel Assembly.
And also, memory allocation is not the most obvious topic in Go/Golang assembly...
No no thats just my bitcoin miner booting up
Also, this is another really good reason never to use a sha2 as your hashing algo for password storage.
> Also, this is another really good reason never to use a sha2 as your hashing algo for password storage.
I don't know what you mean by this. I understand that SHA2 is poor for some things, but I'm not sure how this illustrates that.
SHA2 just got even faster, so it illustrates rather well that you wouldn't want to use it for password storage -- of course that was always true, but the improvement drives that point home.
SHA2 has not gotten faster. It was already blisteringly, unbelievably fast.
"We estimate that on [circa-2009] hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against PBKDF2."
Specifically, PBKDF2 is a tool to produce mostly-okay password storage hash from crypto primitives that are totally unfit for it, while bcrypt, scrypt, Argon2 are purpose-designed to make certain kinds of attacks difficult, like time-memory tradeoffs, parallelized custom hardware attacks, etc.
Ideally you'd use something like Argon2 or any of the other finalists from the Password Hashing Competition[1].
PBKDF2-SHA2 with a reasonably high amount of rounds (>100000) is still okay-ish, although you should have transited to bcrypt years ago. And hopefully we'll see argon2 stabilizing and getting adoption soon™.
I don't know what you mean by this. I understand that
SHA2 is poor for some things, but I'm not sure how this
illustrates that.
You want password hashes to be very slow, (and to use a lot of RAM) to make brute force attacks difficult. SHA2 is fast, and having hardware support makes it even faster. This makes it a bad choice for a password hash.