1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:34:44 -07:00

crypto/elliptic: reduce allocations on amd64

This is inspired by
https://blog.cloudflare.com/go-dont-collect-my-garbage/
This CL adds allocation tracking and parallelizes p256-related benchmarks.
Amount of allocations can be significantly reduced by marking amd64 asm
functions as noescape. This exposes a bug in p256MovCond:
PANDN with memory argument will fault if memory is not aligned, so they
are replaced with MOVDQU (which is ok with unaligned memory) and
register version of PANDN.

Results on 88-thread machine (2x 22 cores) below:
crypto/elliptic:
name               old time/op    new time/op    delta
BaseMultP256-88      1.50µs ±11%    1.19µs ± 5%  -20.20%  (p=0.000 n=10+10)
ScalarMultP256-88    5.47µs ± 5%    3.63µs ±10%  -33.66%  (p=0.000 n=9+10)

name               old alloc/op   new alloc/op   delta
BaseMultP256-88        800B ± 0%      288B ± 0%  -64.00%  (p=0.000 n=10+10)
ScalarMultP256-88    2.59kB ± 0%    0.26kB ± 0%  -90.12%  (p=0.000 n=10+10)

name               old allocs/op  new allocs/op  delta
BaseMultP256-88        13.0 ± 0%       6.0 ± 0%  -53.85%  (p=0.000 n=10+10)
ScalarMultP256-88      16.0 ± 0%       5.0 ± 0%  -68.75%  (p=0.000 n=10+10)

crypto/ecdsa:
name              old time/op    new time/op    delta
SignP256-88         8.63µs ±37%    7.55µs ±38%     ~     (p=0.393 n=10+10)
VerifyP256-88       13.9µs ± 8%     7.0µs ± 7%  -49.29%  (p=0.000 n=10+9)
KeyGeneration-88    2.77µs ±11%    2.34µs ±11%  -15.57%  (p=0.000 n=10+10)

name              old alloc/op   new alloc/op   delta
SignP256-88         4.14kB ± 1%    2.98kB ± 2%  -27.94%  (p=0.000 n=10+10)
VerifyP256-88       4.47kB ± 0%    0.99kB ± 0%  -77.84%  (p=0.000 n=9+10)
KeyGeneration-88    1.21kB ± 0%    0.69kB ± 0%  -42.78%  (p=0.000 n=10+10)

name              old allocs/op  new allocs/op  delta
SignP256-88           47.0 ± 0%      34.0 ± 0%  -27.66%  (p=0.000 n=10+10)
VerifyP256-88         38.0 ± 0%      17.0 ± 0%  -55.26%  (p=0.000 n=10+10)
KeyGeneration-88      20.0 ± 0%      13.0 ± 0%  -35.00%  (p=0.000 n=10+10)

On machine with only 4 cores, results are much less impressive:
around 2% performance gain.

Change-Id: I8a2f8168f83d27ad9ace1b4b1a1e11cb83edf717
Reviewed-on: https://go-review.googlesource.com/80757
Run-TryBot: Ilya Tocar <ilya.tocar@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Ilya Tocar 2017-11-29 13:20:08 -06:00 committed by Brad Fitzpatrick
parent 1992893307
commit 73f284e2f2
4 changed files with 70 additions and 27 deletions

View File

@ -48,10 +48,13 @@ func BenchmarkSignP256(b *testing.B) {
hashed := []byte("testing") hashed := []byte("testing")
priv, _ := GenerateKey(p256, rand.Reader) priv, _ := GenerateKey(p256, rand.Reader)
b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
_, _, _ = Sign(rand.Reader, priv, hashed) for pb.Next() {
} _, _, _ = Sign(rand.Reader, priv, hashed)
}
})
} }
func BenchmarkSignP384(b *testing.B) { func BenchmarkSignP384(b *testing.B) {
@ -60,10 +63,13 @@ func BenchmarkSignP384(b *testing.B) {
hashed := []byte("testing") hashed := []byte("testing")
priv, _ := GenerateKey(p384, rand.Reader) priv, _ := GenerateKey(p384, rand.Reader)
b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
_, _, _ = Sign(rand.Reader, priv, hashed) for pb.Next() {
} _, _, _ = Sign(rand.Reader, priv, hashed)
}
})
} }
func BenchmarkVerifyP256(b *testing.B) { func BenchmarkVerifyP256(b *testing.B) {
@ -73,20 +79,26 @@ func BenchmarkVerifyP256(b *testing.B) {
priv, _ := GenerateKey(p256, rand.Reader) priv, _ := GenerateKey(p256, rand.Reader)
r, s, _ := Sign(rand.Reader, priv, hashed) r, s, _ := Sign(rand.Reader, priv, hashed)
b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
Verify(&priv.PublicKey, hashed, r, s) for pb.Next() {
} Verify(&priv.PublicKey, hashed, r, s)
}
})
} }
func BenchmarkKeyGeneration(b *testing.B) { func BenchmarkKeyGeneration(b *testing.B) {
b.ResetTimer() b.ResetTimer()
p256 := elliptic.P256() p256 := elliptic.P256()
b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
GenerateKey(p256, rand.Reader) for pb.Next() {
} GenerateKey(p256, rand.Reader)
}
})
} }
func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) { func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) {

View File

@ -523,10 +523,13 @@ func BenchmarkBaseMult(b *testing.B) {
p224 := P224() p224 := P224()
e := p224BaseMultTests[25] e := p224BaseMultTests[25]
k, _ := new(big.Int).SetString(e.k, 10) k, _ := new(big.Int).SetString(e.k, 10)
b.ReportAllocs()
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
p224.ScalarBaseMult(k.Bytes()) for pb.Next() {
} p224.ScalarBaseMult(k.Bytes())
}
})
} }
func BenchmarkBaseMultP256(b *testing.B) { func BenchmarkBaseMultP256(b *testing.B) {
@ -534,10 +537,13 @@ func BenchmarkBaseMultP256(b *testing.B) {
p256 := P256() p256 := P256()
e := p224BaseMultTests[25] e := p224BaseMultTests[25]
k, _ := new(big.Int).SetString(e.k, 10) k, _ := new(big.Int).SetString(e.k, 10)
b.ReportAllocs()
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
p256.ScalarBaseMult(k.Bytes()) for pb.Next() {
} p256.ScalarBaseMult(k.Bytes())
}
})
} }
func BenchmarkScalarMultP256(b *testing.B) { func BenchmarkScalarMultP256(b *testing.B) {
@ -546,10 +552,13 @@ func BenchmarkScalarMultP256(b *testing.B) {
_, x, y, _ := GenerateKey(p256, rand.Reader) _, x, y, _ := GenerateKey(p256, rand.Reader)
priv, _, _, _ := GenerateKey(p256, rand.Reader) priv, _, _, _ := GenerateKey(p256, rand.Reader)
b.ReportAllocs()
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { b.RunParallel(func(pb *testing.PB) {
p256.ScalarMult(x, y, priv) for pb.Next() {
} p256.ScalarMult(x, y, priv)
}
})
} }
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {

View File

@ -52,46 +52,62 @@ func (curve p256Curve) Params() *CurveParams {
// Functions implemented in p256_asm_amd64.s // Functions implemented in p256_asm_amd64.s
// Montgomery multiplication modulo P256 // Montgomery multiplication modulo P256
//go:noescape
func p256Mul(res, in1, in2 []uint64) func p256Mul(res, in1, in2 []uint64)
// Montgomery square modulo P256 // Montgomery square modulo P256
//go:noescape
func p256Sqr(res, in []uint64) func p256Sqr(res, in []uint64)
// Montgomery multiplication by 1 // Montgomery multiplication by 1
//go:noescape
func p256FromMont(res, in []uint64) func p256FromMont(res, in []uint64)
// iff cond == 1 val <- -val // iff cond == 1 val <- -val
//go:noescape
func p256NegCond(val []uint64, cond int) func p256NegCond(val []uint64, cond int)
// if cond == 0 res <- b; else res <- a // if cond == 0 res <- b; else res <- a
//go:noescape
func p256MovCond(res, a, b []uint64, cond int) func p256MovCond(res, a, b []uint64, cond int)
// Endianness swap // Endianness swap
//go:noescape
func p256BigToLittle(res []uint64, in []byte) func p256BigToLittle(res []uint64, in []byte)
//go:noescape
func p256LittleToBig(res []byte, in []uint64) func p256LittleToBig(res []byte, in []uint64)
// Constant time table access // Constant time table access
//go:noescape
func p256Select(point, table []uint64, idx int) func p256Select(point, table []uint64, idx int)
//go:noescape
func p256SelectBase(point, table []uint64, idx int) func p256SelectBase(point, table []uint64, idx int)
// Montgomery multiplication modulo Ord(G) // Montgomery multiplication modulo Ord(G)
//go:noescape
func p256OrdMul(res, in1, in2 []uint64) func p256OrdMul(res, in1, in2 []uint64)
// Montgomery square modulo Ord(G), repeated n times // Montgomery square modulo Ord(G), repeated n times
//go:noescape
func p256OrdSqr(res, in []uint64, n int) func p256OrdSqr(res, in []uint64, n int)
// Point add with in2 being affine point // Point add with in2 being affine point
// If sign == 1 -> in2 = -in2 // If sign == 1 -> in2 = -in2
// If sel == 0 -> res = in1 // If sel == 0 -> res = in1
// if zero == 0 -> res = in2 // if zero == 0 -> res = in2
//go:noescape
func p256PointAddAffineAsm(res, in1, in2 []uint64, sign, sel, zero int) func p256PointAddAffineAsm(res, in1, in2 []uint64, sign, sel, zero int)
// Point add. Returns one if the two input points were equal and zero // Point add. Returns one if the two input points were equal and zero
// otherwise. (Note that, due to the way that the equations work out, some // otherwise. (Note that, due to the way that the equations work out, some
// representations of ∞ are considered equal to everything by this function.) // representations of ∞ are considered equal to everything by this function.)
//go:noescape
func p256PointAddAsm(res, in1, in2 []uint64) int func p256PointAddAsm(res, in1, in2 []uint64) int
// Point double // Point double
//go:noescape
func p256PointDoubleAsm(res, in []uint64) func p256PointDoubleAsm(res, in []uint64)
func (curve p256Curve) Inverse(k *big.Int) *big.Int { func (curve p256Curve) Inverse(k *big.Int) *big.Int {

View File

@ -81,17 +81,23 @@ TEXT ·p256MovCond(SB),NOSPLIT,$0
PCMPEQL X13, X12 PCMPEQL X13, X12
MOVOU X12, X0 MOVOU X12, X0
PANDN (16*0)(x_ptr), X0 MOVOU (16*0)(x_ptr), X6
PANDN X6, X0
MOVOU X12, X1 MOVOU X12, X1
PANDN (16*1)(x_ptr), X1 MOVOU (16*1)(x_ptr), X7
PANDN X7, X1
MOVOU X12, X2 MOVOU X12, X2
PANDN (16*2)(x_ptr), X2 MOVOU (16*2)(x_ptr), X8
PANDN X8, X2
MOVOU X12, X3 MOVOU X12, X3
PANDN (16*3)(x_ptr), X3 MOVOU (16*3)(x_ptr), X9
PANDN X9, X3
MOVOU X12, X4 MOVOU X12, X4
PANDN (16*4)(x_ptr), X4 MOVOU (16*4)(x_ptr), X10
PANDN X10, X4
MOVOU X12, X5 MOVOU X12, X5
PANDN (16*5)(x_ptr), X5 MOVOU (16*5)(x_ptr), X11
PANDN X11, X5
MOVOU (16*0)(y_ptr), X6 MOVOU (16*0)(y_ptr), X6
MOVOU (16*1)(y_ptr), X7 MOVOU (16*1)(y_ptr), X7