From f35555964d6a3771147c9072f928b3ca8cad26fe Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 13 Aug 2020 02:23:52 +0200 Subject: [PATCH] Better "optimal selection Signed-off-by: Jakub Sztandera --- chain/messagepool/block_proba.go | 33 +++++++++++++++--------- chain/messagepool/selection.go | 40 +++++++++++++++++++---------- chain/messagepool/selection_test.go | 35 ++++++++++++------------- 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/chain/messagepool/block_proba.go b/chain/messagepool/block_proba.go index 403507352..7304bd3ce 100644 --- a/chain/messagepool/block_proba.go +++ b/chain/messagepool/block_proba.go @@ -1,20 +1,29 @@ package messagepool -import "math" +import ( + "math" + "sync" +) + +var noWinnersProbCache []float64 +var noWinnersProbOnce sync.Once func noWinnersProb() []float64 { - poissPdf := func(x float64) float64 { - const Mu = 5 - lg, _ := math.Lgamma(x + 1) - result := math.Exp((math.Log(Mu) * x) - lg - Mu) - return result - } + noWinnersProbOnce.Do(func() { + poissPdf := func(x float64) float64 { + const Mu = 5 + lg, _ := math.Lgamma(x + 1) + result := math.Exp((math.Log(Mu) * x) - lg - Mu) + return result + } - out := make([]float64, 0, MaxBlocks) - for i := 0; i < MaxBlocks; i++ { - out = append(out, poissPdf(float64(i))) - } - return out + out := make([]float64, 0, MaxBlocks) + for i := 0; i < MaxBlocks; i++ { + out = append(out, poissPdf(float64(i))) + } + noWinnersProbCache = out + }) + return noWinnersProbCache } func binomialCoefficient(n, k float64) float64 { diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index a70cb7021..e38daf150 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -22,16 +22,17 @@ var bigBlockGasLimit = big.NewInt(build.BlockGasLimit) const MaxBlocks = 15 type msgChain struct { - msgs []*types.SignedMessage - gasReward *big.Int - gasLimit int64 - gasPerf float64 - effPerf float64 - bp float64 - valid bool - merged bool - next *msgChain - prev *msgChain + msgs []*types.SignedMessage + gasReward *big.Int + gasLimit int64 + gasPerf float64 + effPerf float64 + bp float64 + parentOffset float64 + valid bool + merged bool + next *msgChain + prev *msgChain } func (mp *MessagePool) SelectMessages(ts *types.TipSet, tq float64) ([]*types.SignedMessage, error) { @@ -153,7 +154,7 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 last := len(chains) for i, chain := range chains { // did we run out of performing chains? - if chain.gasPerf < 0 { + if chain.effPerf < 0 { break } @@ -176,12 +177,22 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 for i := len(chainDeps) - 1; i >= 0; i-- { curChain := chainDeps[i] curChain.merged = true + if next := curChain.next; next != nil { + next.effPerf += next.parentOffset + } result = append(result, curChain.msgs...) } chain.merged = true + if next := chain.next; next != nil { + next.effPerf += next.parentOffset + } result = append(result, chain.msgs...) gasLimit -= chainGasLimit + + sort.Slice(chains[i+1:], func(i, j int) bool { + return chains[i].BeforeEffective(chains[j]) + }) continue } @@ -852,7 +863,9 @@ func (mc *msgChain) SetEffectivePerf(bp float64) { func (mc *msgChain) setEffPerf() { effPerf := mc.gasPerf * mc.bp if mc.prev != nil { - effPerf = (effPerf*float64(mc.gasLimit) + mc.prev.effPerf*float64(mc.prev.gasLimit)) / float64(mc.gasLimit+mc.prev.gasLimit) + effPerfWithParent := (effPerf*float64(mc.gasLimit) + mc.prev.effPerf*float64(mc.prev.gasLimit)) / float64(mc.gasLimit+mc.prev.gasLimit) + mc.parentOffset = effPerf - effPerfWithParent + effPerf = effPerfWithParent } mc.effPerf = effPerf @@ -867,7 +880,8 @@ func (mc *msgChain) SetNullEffectivePerf() { } func (mc *msgChain) BeforeEffective(other *msgChain) bool { - return mc.effPerf > other.effPerf || + // moved merged chains to the front so we can discard them earlier + return (mc.merged && !other.merged) || mc.effPerf > other.effPerf || (mc.effPerf == other.effPerf && mc.gasPerf > other.gasPerf) || (mc.effPerf == other.effPerf && mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0) } diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index fb2e4cc5f..28909b78f 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -919,20 +919,20 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand) { t.Fatal(err) } - // 2. optimal selection - minersRand := rng.Float64() - winerProba := noWinnersProb() - i := 0 - for ; i < MaxBlocks && minersRand > 0; i++ { - minersRand -= winerProba[i] - } - nMiners := i - if nMiners == 0 { - nMiners = 1 - } - logging.SetLogLevel("messagepool", "error") - for i := 0; i < 1; i++ { + for i := 0; i < 50; i++ { + // 2. optimal selection + minersRand := rng.Float64() + winerProba := noWinnersProb() + i := 0 + for ; i < MaxBlocks && minersRand > 0; i++ { + minersRand -= winerProba[i] + } + nMiners := i + if nMiners == 0 { + nMiners = 1 + } + optMsgs := make(map[cid.Cid]*types.SignedMessage) for j := 0; j < nMiners; j++ { tq := rng.Float64() @@ -946,7 +946,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand) { } t.Logf("nMiners: %d", nMiners) - t.Logf("greedy capacity %d, optimal capacity %d (x%.1f)", len(greedyMsgs), + t.Logf("greedy capacity %d, optimal capacity %d (x %.1f )", len(greedyMsgs), len(optMsgs), float64(len(optMsgs))/float64(len(greedyMsgs))) if len(greedyMsgs) > len(optMsgs) { t.Fatal("greedy capacity higher than optimal capacity; wtf") @@ -965,18 +965,15 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand) { nMinersBig := big.NewInt(int64(nMiners)) greedyAvgReward, _ := new(big.Rat).SetFrac(greedyReward, nMinersBig).Float64() optimalAvgReward, _ := new(big.Rat).SetFrac(optReward, nMinersBig).Float64() - t.Logf("greedy reward: %.0f, optimal reward: %.0f (x%.1f)", greedyAvgReward, + t.Logf("greedy reward: %.0f, optimal reward: %.0f (x %.1f )", greedyAvgReward, optimalAvgReward, optimalAvgReward/greedyAvgReward) - if greedyReward.Cmp(optReward) > 0 { - t.Fatal("greedy reward raw higher than optimal reward; booh") - } } logging.SetLogLevel("messagepool", "info") } func TestCompetitiveMessageSelection(t *testing.T) { - seeds := []int64{1947, 1976, 2020, 2100, 10000} + seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45} for _, seed := range seeds { t.Log("running competitve message selection with seed", seed) testCompetitiveMessageSelection(t, rand.New(rand.NewSource(seed)))