On Mon Apr 5, 2021 at 2:39 AM CDT, Niels Möller wrote:
> "Christopher M. Riedl" <[email protected]> writes:
>
> > An implementation combining AES+GCM _can potentially_ yield significant
> > performance boosts by allowing for increased instruction parallelism,
> > avoiding
> > C-function call overhead, more flexibility in assembly fine-tuning, etc.
> > This
> > series provides such an implementation based on the existing optimized
> > Nettle
> > routines for POWER9 and later processors. Benchmark results on a POWER9
> > Blackbird running at 3.5GHz are given at the end of this mail.
>
> Benchmark results are impressive. If I get the numbers right, cycles per
> block (16 bytes) is reduced from 40 to 22.5. You can run
> nettle-benchmark with the flag -f 3.5e9 (for 3.5GHz clock frequency) to
> get cycle numbers in the output.
Hi Niels,
Your math is very close - here are benchmark results for encrypt (since
decrypt is essentially the same):
AES+GCM combined (this series)
------------------------------
Algorithm mode Mbyte/s cycles/byte cycles/block
gcm_aes128 encrypt 2564.32 1.30 20.83
gcm_aes192 encrypt 2276.86 1.47 23.46
gcm_aes256 encrypt 2051.87 1.63 26.03
AES,GCM separate (nettle master)
--------------------------------
Algorithm mode Mbyte/s cycles/byte cycles/block
gcm_aes128 encrypt 1419.17 2.35 37.63
gcm_aes192 encrypt 1313.69 2.54 40.65
gcm_aes256 encrypt 1218.79 2.74 43.82
So for aes128: 37.63 - 20.83 = 16.80 cycles/block improvement.
>
> I'm a bit conservative about about adding assembly code for combined
> operations, since it can lead to an explosion in the amount of code to
> maintain. So I'd like to understand a bit better where the 17.5 saved
> cycles were spent. For the code on master, gcm_encrypt (with aes) is
> built from
> these building blocks:
Makes perfect sense to me!
>
> * gcm_fill
>
> C code, essentially 2 64-bit stores per block. On little endian, it
> also needs some byte swapping.
>
> * aes_encrypt
>
> Using power assembly. Performance measured as the "aes128 ECB
> encrypt" line in nettle-benchmark output.
>
> * memxor3
>
> This is C code on power (and rather hairy C code). Performance can
> be measured with nettle-benchmark, and it's going to be a bit
> alignment dependent.
>
> * gcm_hash
>
> This uses power assembly. Performance is measured as the "gcm
> update" line in nettle-benchmark output. From your numbers, this
> seems to be 7.3 cycles per block.
>
> So before going all the way with a combined aes_gcm function, I think
> it's good to try to optimize the building blocks. Please benchmark
> memxor3, to see if it could benefit from assembly implementation. If so,
> that should give a nice speedup to several modes, not just gcm. (If you
> implement memxor3, beware that it needs to support some overlap, to not
> break in-place CBC decrypt).
The benchmark results don't convince me memxor3 and memxor are actually
a huge bottleneck by themselves. It does appear to show that my combined
implementation is dominated by the cost of AES (which matches when I run
a simple test encrypt program with the 'perf' utility):
Algorithm mode Mbyte/s cycles/byte cycles/block
memxor aligned 16634.14 0.20 1.61
memxor unaligned 11089.33 0.30 2.41
memxor3 aligned 17261.19 0.19 1.55
memxor3 unaligned01 11549.04 0.29 2.31
memxor3 unaligned11 11181.62 0.30 2.39
memxor3 unaligned12 8485.88 0.39 3.15
aes128 ECB encrypt 2762.38 1.21 19.33
aes128 ECB decrypt 2203.65 1.51 24.24
I tried a few other experiments:
1. Replace memxor/3 with a no-op function (ie. just 'return'):
Algorithm mode Mbyte/s cycles/byte cycles/block
gcm_aes128 encrypt 1553.08 2.15 34.39
gcm_aes192 encrypt 1428.57 2.34 37.38
gcm_aes256 encrypt 1318.05 2.53 40.52
aes128: 37.63 - 34.39 = 3.24 cycles/block
2. Replace memxor/3,gcm_fill w/ a no-op function:
Algorithm mode Mbyte/s cycles/byte cycles/block
gcm_aes128 encrypt 1793.37 1.86 29.78
gcm_aes192 encrypt 1625.74 2.05 32.85
gcm_aes256 encrypt 1483.81 2.25 35.99
aes128: 34.39 - 29.78 = 4.61 cycles/block
3. Replace memxor/3, and gcm_fill w/ no-op functions and use POWER9
instructions lxvb16x/stxvb16x to load/store unaligned vectors and
avoid the permutes on LE:
Algorithm mode Mbyte/s cycles/byte cycles/block
gcm_aes128 encrypt 2069.67 1.61 25.80
gcm_aes192 encrypt 1875.97 1.78 28.47
gcm_aes256 encrypt 1717.33 1.94 31.10
aes128: 29.78 - 25.80 = 3.98 cycles/block
So in total, if we assume an ideal (but impossible) zero-cost version
for memxor, memxor3, and gcm_fill and avoid permutes via ISA 3.0 vector
load/stores we can only account for 11.82 cycles/block; leaving 4.97
cycles/block as an additional benefit of the combined implementation.
But all this assumes zero-cost implementations of these building blocks
so the improvement of the combined implementation is >5 cycles/block.
>
> Another potential overhead is that data is stored to memory when passed
> between these functions. It seems we store a block 3 times, and loads a
> block 4 times (the additional accesses should be cache friendly, but
> wills till cost some execution resources). Optimizing that seems to need
> some kind of combined function. But maybe it is sufficient to optimize
> something a bit more general than aes gcm, e.g., aes ctr?
This would basically have to replace the nettle_crypt16 function call
with arch-specific assembly, right? I can code this up and try it out in
the context of AES-GCM.
Thanks!
Chris R.
>
> Regards,
> /Niels
>
> --
> Niels Möller. PGP-encrypted email is preferred. Keyid 368C6677.
> Internet email is subject to wholesale government surveillance.
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs