From 4eb02f1dd96e136b0f7cf7eff792da1e44176713 Mon Sep 17 00:00:00 2001 From: haoping-ku Date: Wed, 5 Dec 2018 17:38:03 +0800 Subject: Haoping fix simulation (#356) * simulation: add benchmark features * tmp * simulation: modify Debug interface * Added BlockReceived and BlockReady function to Debug interface. * Added Benchmark features. * fix * fix typos --- simulation/app.go | 66 ++++++++++++++++++++++++++++++++++++++++------- simulation/peer-server.go | 34 ++++++++++++++++++++++++ simulation/utils.go | 36 ++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 simulation/utils.go (limited to 'simulation') diff --git a/simulation/app.go b/simulation/app.go index af271e1..74bd6cd 100644 --- a/simulation/app.go +++ b/simulation/app.go @@ -44,22 +44,41 @@ type timestampMessage struct { Timestamp time.Time `json:"timestamp"` } +const ( + // Block received or created in agreement. + blockEventReceived int = iota + // Block confirmed in agreement, sent into lattice + blockEventConfirmed + // Block delivered by lattice. + blockEventDelivered + // block is ready (Randomness calculated) + blockEventReady + + blockEventCount +) + +type blockEventMessage struct { + BlockHash common.Hash `json:"hash"` + Timestamps []time.Time `json:"timestamps"` +} + // simApp is an DEXON app for simulation. type simApp struct { - NodeID types.NodeID - Outputs []*types.Block - Early bool - netModule *test.Network - stateModule *test.State - DeliverID int - // blockSeen stores the time when block is delivered by Total Ordering. - blockSeen map[common.Hash]time.Time + NodeID types.NodeID + Outputs []*types.Block + Early bool + netModule *test.Network + stateModule *test.State + DeliverID int + blockTimestamps map[common.Hash][]time.Time + blockSeen map[common.Hash]time.Time // uncofirmBlocks stores the blocks whose timestamps are not ready. unconfirmedBlocks map[types.NodeID]common.Hashes blockByHash map[common.Hash]*types.Block blockByHashMutex sync.RWMutex latestWitness types.Witness latestWitnessReady *sync.Cond + lock sync.RWMutex } // newSimApp returns point to a new instance of simApp. @@ -71,6 +90,7 @@ func newSimApp( stateModule: stateModule, DeliverID: 0, blockSeen: make(map[common.Hash]time.Time), + blockTimestamps: make(map[common.Hash][]time.Time), unconfirmedBlocks: make(map[types.NodeID]common.Hashes), blockByHash: make(map[common.Hash]*types.Block), latestWitnessReady: sync.NewCond(&sync.Mutex{}), @@ -84,6 +104,7 @@ func (a *simApp) BlockConfirmed(block types.Block) { // TODO(jimmy-dexon) : Remove block in this hash if it's no longer needed. a.blockByHash[block.Hash] = &block a.blockSeen[block.Hash] = time.Now().UTC() + a.updateBlockEvent(block.Hash) } // VerifyBlock implements core.Application. @@ -139,7 +160,7 @@ func (a *simApp) TotalOrderingDelivered( fmt.Println("OUTPUT", a.NodeID, mode, blockHashes) latencies := []time.Duration{} for _, h := range blockHashes { - latencies = append(latencies, time.Since(a.blockSeen[h])) + latencies = append(latencies, time.Since(a.blockTimestamps[h][blockEventConfirmed])) } blockList := &BlockList{ ID: a.DeliverID, @@ -153,6 +174,7 @@ func (a *simApp) TotalOrderingDelivered( // BlockDelivered is called when a block in compaction chain is delivered. func (a *simApp) BlockDelivered( blockHash common.Hash, pos types.Position, result types.FinalizationResult) { + if len(result.Randomness) == 0 && pos.Round > 0 { panic(fmt.Errorf("Block %s randomness is empty", blockHash)) } @@ -178,6 +200,8 @@ func (a *simApp) BlockDelivered( a.latestWitnessReady.Broadcast() }() + a.updateBlockEvent(blockHash) + seenTime, exist := a.blockSeen[blockHash] if !exist { return @@ -206,3 +230,27 @@ func (a *simApp) BlockDelivered( } a.netModule.Report(msg) } + +// BlockReceived is called when a block is received in agreement. +func (a *simApp) BlockReceived(hash common.Hash) { + a.updateBlockEvent(hash) +} + +// BlockReady is called when a block is ready. +func (a *simApp) BlockReady(hash common.Hash) { + a.updateBlockEvent(hash) +} + +func (a *simApp) updateBlockEvent(hash common.Hash) { + a.lock.Lock() + defer a.lock.Unlock() + a.blockTimestamps[hash] = append(a.blockTimestamps[hash], time.Now().UTC()) + if len(a.blockTimestamps[hash]) == blockEventCount { + msg := &blockEventMessage{ + BlockHash: hash, + Timestamps: a.blockTimestamps[hash], + } + a.netModule.Report(msg) + delete(a.blockTimestamps, hash) + } +} diff --git a/simulation/peer-server.go b/simulation/peer-server.go index 1cd6ca1..95ce5c1 100644 --- a/simulation/peer-server.go +++ b/simulation/peer-server.go @@ -24,7 +24,9 @@ import ( "log" "reflect" "sync" + "time" + "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" "github.com/dexon-foundation/dexon-consensus/simulation/config" @@ -41,6 +43,7 @@ type PeerServer struct { cfg *config.Config ctx context.Context ctxCancel context.CancelFunc + blockEvents map[types.NodeID]map[common.Hash][]time.Time } // NewPeerServer returns a new PeerServer instance. @@ -51,6 +54,7 @@ func NewPeerServer() *PeerServer { peerTotalOrder: make(PeerTotalOrder), ctx: ctx, ctxCancel: cancel, + blockEvents: make(map[types.NodeID]map[common.Hash][]time.Time), } } @@ -114,6 +118,17 @@ func (p *PeerServer) handleMessage(id types.NodeID, m *message) { } } +func (p *PeerServer) handleBlockEventMessage(id types.NodeID, msg *blockEventMessage) { + if _, exist := p.blockEvents[id]; !exist { + p.blockEvents[id] = make(map[common.Hash][]time.Time) + } + nodeEvents := p.blockEvents[id] + if _, exist := nodeEvents[msg.BlockHash]; !exist { + nodeEvents[msg.BlockHash] = []time.Time{} + } + nodeEvents[msg.BlockHash] = msg.Timestamps +} + func (p *PeerServer) mainLoop() { for { select { @@ -134,6 +149,8 @@ func (p *PeerServer) mainLoop() { p.handleBlockList(e.From, val) case *message: p.handleMessage(e.From, val) + case *blockEventMessage: + p.handleBlockEventMessage(e.From, val) default: panic(fmt.Errorf("unknown message: %v", reflect.TypeOf(e.Msg))) } @@ -182,4 +199,21 @@ func (p *PeerServer) Run() { if err := p.trans.Close(); err != nil { log.Printf("Error shutting down peerServer: %v\n", err) } + p.logBlockEvents() +} + +func (p *PeerServer) logBlockEvents() { + diffs := [blockEventCount - 1][]float64{} + for _, bs := range p.blockEvents { + for _, ts := range bs { + for i := 0; i < blockEventCount-1; i++ { + diffs[i] = append(diffs[i], float64(ts[i+1].Sub(ts[i]))/1000000000) + } + } + } + log.Printf("===== block event mean and std dev (%d blocks) =====\n", len(diffs[0])) + for i, a := range diffs { + m, d := calcMeanAndStdDeviation(a) + log.Printf(" event %d: mean = %f, std dev = %f\n", i, m, d) + } } diff --git a/simulation/utils.go b/simulation/utils.go new file mode 100644 index 0000000..f0d864b --- /dev/null +++ b/simulation/utils.go @@ -0,0 +1,36 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// . + +package simulation + +import ( + "math" +) + +func calcMeanAndStdDeviation(a []float64) (float64, float64) { + sum := float64(0) + for _, i := range a { + sum += i + } + mean := sum / float64(len(a)) + dev := float64(0) + for _, i := range a { + dev += (i - mean) * (i - mean) + } + dev = math.Sqrt(dev / float64(len(a))) + return mean, dev +} -- cgit v1.2.3