Thanks to visit codestin.com
Credit goes to github.com

Skip to content

64-bit integer arithmetic performance optimization #1082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 1, 2021

Conversation

nevkontakte
Copy link
Member

This PR has two changes aimed on improving math performance under GopherJS:

  • Optimized 64-bit int multiplication algorithm, ~15x faster than the original one. It is still orders of magnitude slower than native 64-bit operations on a modern CPU, but it improves crypto/ed25519 performance quite a bit.

    name            old time/op  new time/op  delta
    KeyGeneration   29.9ms ± 0%  15.6ms ± 1%  -47.79%  (p=0.008 n=5+5)
    NewKeyFromSeed  30.1ms ± 1%  15.7ms ± 2%  -47.89%  (p=0.008 n=5+5)
    Signing         38.3ms ± 1%  20.2ms ± 2%  -47.31%  (p=0.008 n=5+5)
    Verification     110ms ± 1%    52ms ± 1%  -52.47%  (p=0.008 n=5+5)
    

    I think there is some potential for further improvement in adopting JavaScript's BigInt for 64-bit math, but browser support for it is far from universal, and it will require significant changes to the compiler

  • Removed use of 64-bit ints in math/bits package where possible. This also improved performance of Add32, Mul32, Div32, etc. functions by 10-20x. This had improved performance of asymmetric crypto packages, such as crypto/rsa:

    name                  old time/op  new time/op  delta
    RSA2048Decrypt         1.61s ± 0%   0.18s ± 2%  -88.77%  (p=0.008 n=5+5)
    RSA2048Sign            1.68s ± 3%   0.19s ± 2%  -88.56%  (p=0.008 n=5+5)
    3PrimeRSA2048Decrypt   846ms ± 2%   123ms ± 1%  -85.48%  (p=0.008 n=5+5)
    

Some more specific benchmarks are available in commit comments. Overall this reduced standard library tests execution time on CI from 14 to 7 minutes, which was my main motivation :)

This should provide me with the means of testing performance and
correctness of the optimized int64 multiplication algorithm.
Instead of doing bit-by-bit iteration, we split multipliers into 4
16-bit words each and perform long multiplication algorithm on them. A
slight advantage of this algorithm is that it is constant-time, which is
good for cryptographic purposes (although there are probably plenty of
other side channels still).

Optimized algorithm is ~15x faster if you adjust for the cost of array
access:

```
name            old time/op  new time/op  delta
Mul64/noop      3.70ns ± 0%  3.73ns ± 1%   +0.60%  (p=0.000 n=17+17)
Mul64/unsigned   380ns ± 0%    29ns ± 1%  -92.44%  (p=0.000 n=17+20)
Mul64/signed     402ns ± 0%    30ns ± 2%  -92.63%  (p=0.000 n=17+20)
```

This is still several orders of magnitude slower than native 64-bit
multiplication, and at this point the cost is dominated by allocation of
new int64 container objects, which is pretty much unavoidable in
JavaScript.
This test requires regular Go and panics under GopherJS.
In the JavaScript context falling back to 64-bit arithmetics leads to
performance significantly worse that an algorithm using native 32-bit
math, since under the hood those operations are transformed into an
algorithm with 32-bit math, but for a more general case and therefore
a lot slower. Plus inefficiency of our 64-bit representation.

This change shows a significant improvement on `math/bits` benchmarks:

```
Add32            12.7ns ± 0%   1.7ns ± 0%  -86.40%  (p=0.000 n=18+19)
Mul32            58.8ns ± 2%   2.0ns ± 0%  -96.54%  (p=0.000 n=20+18)
Div32             123ns ± 1%    30ns ± 1%  -75.82%  (p=0.000 n=20+19)
``

Note that performance of `math/bits.Add()` and similar have also
improved, since on 32-bit architectures they correspond to 32-bit
valiants. Performance of 64-bit ops remains unaffected by this change
(but did benefit from earlier optimizations).
@nevkontakte nevkontakte added this to the Runtime performance milestone Nov 1, 2021
@nevkontakte nevkontakte requested a review from flimzy November 1, 2021 19:44
@nevkontakte nevkontakte enabled auto-merge November 1, 2021 19:44
Copy link
Member

@flimzy flimzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!

@nevkontakte nevkontakte merged commit f9bde24 into gopherjs:master Nov 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants