Skip to content

Commit

Permalink
Improve perfomance of ComputeWinCount
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Sztandera committed Jun 22, 2020
1 parent 266c72a commit 8df2e78
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
11 changes: 7 additions & 4 deletions chain/types/electionproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}

Expand Down
26 changes: 23 additions & 3 deletions chain/types/electionproof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 8df2e78

Please sign in to comment.