Improve performance of ComputeWinCount

As it turns out `big.Int#Mul` doesn't like it when you reuse input as
output.

```
name         old time/op    new time/op    delta
WinCounts-8    4.56µs ± 1%    3.90µs ± 2%  -14.44%  (p=0.000 n=9+10)

name         old alloc/op   new alloc/op   delta
WinCounts-8    3.50kB ± 0%    1.15kB ± 0%  -67.23%  (p=0.000 n=10+10)

name         old allocs/op  new allocs/op  delta
WinCounts-8      45.0 ± 0%      24.0 ± 0%  -46.67%  (p=0.000 n=10+10)
```

Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
Jakub Sztandera 2020-06-22 02:21:26 +02:00
parent ee5639aad9
commit f523b17a35
No known key found for this signature in database
GPG Key ID: 9A9AF56F8B3879BA
2 changed files with 30 additions and 7 deletions

View File

@ -83,11 +83,13 @@ func expneg(x *big.Int) *big.Int {
// polyval evaluates a polynomial given by coefficients `p` in Q.256 format,
// at point `x` in Q.256 format, output is in Q.256
func polyval(p []*big.Int, x *big.Int) *big.Int {
res := new(big.Int).Set(p[0]) // Q.256
tmp := new(big.Int) // big.Int.Mul doesn't like when input is reused as output
for _, c := range p[1:] {
res = res.Mul(res, x) // Q.256 * Q.256 => Q.512
res = res.Rsh(res, precision) // Q.512 >> 256 => Q.256
tmp = tmp.Mul(res, x) // Q.256 * Q.256 => Q.512
res = res.Rsh(tmp, precision) // Q.512 >> 256 => Q.256
res = res.Add(res, c)
}
@ -129,12 +131,13 @@ func (ep *ElectionProof) ComputeWinCount(power BigInt, totalPower BigInt) uint64
rhs = rhs.Lsh(rhs, precision) // Q.256
rhs = rhs.Sub(rhs, elam) // Q.256
tmp := new(big.Int) // big.Int.Mul doesn't like when input is reused as output
var j uint64
for lhs.Cmp(rhs) < 0 && j < MaxWinCount {
j++
pmf = pmf.Mul(pmf, lam) // Q.256 * Q.256 => Q.512
pmf = pmf.Rsh(pmf, precision) // Q.512 >> 256 => Q.256
pmf = pmf.Div(pmf, new(big.Int).SetUint64(j) /* Q.0 */) // Q.256 / Q.0 => Q.256
tmp = tmp.Mul(pmf, lam) // Q.256 * Q.256 => Q.512
pmf = pmf.Rsh(tmp, precision) // Q.512 >> 256 => Q.256
rhs = rhs.Sub(rhs, pmf)
}

View File

@ -18,23 +18,43 @@ func TestElectionLam(t *testing.T) {
}
func TestElectionExp(t *testing.T) {
t.Skip()
t.SkipNow()
const N = 256
step := big.NewInt(5)
step = step.Lsh(step, 256) // Q.256
step = step.Div(step, big.NewInt(N-1))
f, _ := os.Create("exp.csv")
x := big.NewInt(0)
for i := 0; i < N; i++ {
y := expneg(x)
fmt.Printf("\"%s\" \"%s\";\n", x, y)
fmt.Fprintf(f, "%s,%s\n", x, y)
x = x.Add(x, step)
}
}
var Res uint64
func BenchmarkWinCounts(b *testing.B) {
totalPower := NewInt(100)
power := NewInt(100)
ep := &ElectionProof{VRFProof: nil}
var res uint64
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
ep.VRFProof = []byte{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24), byte(i >> 32)}
j := ep.ComputeWinCount(power, totalPower)
res += j
}
Res += res
}
func TestWinCounts(t *testing.T) {
t.Skip()
t.SkipNow()
totalPower := NewInt(100)
power := NewInt(30)