From 8df2e78250f4f14dfa20a5815858bdc5e34a9d0f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 22 Jun 2020 02:21:26 +0200 Subject: [PATCH] Improve perfomance of ComputeWinCount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- chain/types/electionproof.go | 11 +++++++---- chain/types/electionproof_test.go | 26 +++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/chain/types/electionproof.go b/chain/types/electionproof.go index c5fe0b8b9a..de99d6fcb4 100644 --- a/chain/types/electionproof.go +++ b/chain/types/electionproof.go @@ -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) } diff --git a/chain/types/electionproof_test.go b/chain/types/electionproof_test.go index 70bc4e0708..713652c618 100644 --- a/chain/types/electionproof_test.go +++ b/chain/types/electionproof_test.go @@ -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)