mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
wip
This commit is contained in:
parent
57b5d2ebcc
commit
15cc2dfe52
1 changed files with 46 additions and 101 deletions
|
|
@ -75,7 +75,7 @@ func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePre
|
||||||
// and reports the stats to the metrics subsystem.
|
// and reports the stats to the metrics subsystem.
|
||||||
func (p *triePrefetcher) close() {
|
func (p *triePrefetcher) close() {
|
||||||
for _, fetcher := range p.fetchers {
|
for _, fetcher := range p.fetchers {
|
||||||
fetcher.abort() // safe to do multiple times
|
fetcher.wait() // safe to do multiple times
|
||||||
|
|
||||||
if metrics.Enabled {
|
if metrics.Enabled {
|
||||||
if fetcher.root == p.root {
|
if fetcher.root == p.root {
|
||||||
|
|
@ -123,24 +123,16 @@ func (p *triePrefetcher) copy() *triePrefetcher {
|
||||||
storageSkipMeter: p.storageSkipMeter,
|
storageSkipMeter: p.storageSkipMeter,
|
||||||
storageWasteMeter: p.storageWasteMeter,
|
storageWasteMeter: p.storageWasteMeter,
|
||||||
}
|
}
|
||||||
// If the prefetcher is already a copy, duplicate the data
|
|
||||||
if p.fetches != nil {
|
|
||||||
for root, fetch := range p.fetches {
|
|
||||||
if fetch == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
copy.fetches[root] = p.db.CopyTrie(fetch)
|
|
||||||
}
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
// Otherwise we're copying an active fetcher, retrieve the current states
|
|
||||||
for id, fetcher := range p.fetchers {
|
|
||||||
copy.fetches[id] = fetcher.peek()
|
|
||||||
}
|
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefetch schedules a batch of trie items to prefetch.
|
// prefetch schedules a batch of trie items to prefetch.
|
||||||
|
// prefetch is called from two locations:
|
||||||
|
// 1. Finalize of the state-objects storage roots. This happens at the end
|
||||||
|
// of every transaction, meaning that if several transactions touches
|
||||||
|
// upon the same contract, the parameters invoking this method may be
|
||||||
|
// repeated.
|
||||||
|
// 2. Finalize of the main account trie. This happens only once per block.
|
||||||
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) {
|
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) {
|
||||||
// If the prefetcher is an inactive one, bail out
|
// If the prefetcher is an inactive one, bail out
|
||||||
if p.fetches != nil {
|
if p.fetches != nil {
|
||||||
|
|
@ -175,15 +167,13 @@ func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
|
||||||
p.deliveryMissMeter.Mark(1)
|
p.deliveryMissMeter.Mark(1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Interrupt the prefetcher if it's by any chance still running and return
|
// Wait for the fether to finish
|
||||||
// a copy of any pre-loaded trie.
|
fetcher.wait() // safe to do multiple times
|
||||||
fetcher.abort() // safe to do multiple times
|
if fetcher.trie == nil {
|
||||||
|
|
||||||
trie := fetcher.peek()
|
|
||||||
if trie == nil {
|
|
||||||
p.deliveryMissMeter.Mark(1)
|
p.deliveryMissMeter.Mark(1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
trie := fetcher.db.CopyTrie(fetcher.trie)
|
||||||
return trie
|
return trie
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +211,6 @@ type subfetcher struct {
|
||||||
wake chan struct{} // Wake channel if a new task is scheduled
|
wake chan struct{} // Wake channel if a new task is scheduled
|
||||||
stop chan struct{} // Channel to interrupt processing
|
stop chan struct{} // Channel to interrupt processing
|
||||||
term chan struct{} // Channel to signal interruption
|
term chan struct{} // Channel to signal interruption
|
||||||
copy chan chan Trie // Channel to request a copy of the current trie
|
|
||||||
|
|
||||||
seen map[string]struct{} // Tracks the entries already loaded
|
seen map[string]struct{} // Tracks the entries already loaded
|
||||||
dups int // Number of duplicate preload tasks
|
dups int // Number of duplicate preload tasks
|
||||||
|
|
@ -238,9 +227,7 @@ func newSubfetcher(db Database, state common.Hash, owner common.Hash, root commo
|
||||||
root: root,
|
root: root,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
wake: make(chan struct{}, 1),
|
wake: make(chan struct{}, 1),
|
||||||
stop: make(chan struct{}),
|
|
||||||
term: make(chan struct{}),
|
term: make(chan struct{}),
|
||||||
copy: make(chan chan Trie),
|
|
||||||
seen: make(map[string]struct{}),
|
seen: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
go sf.loop()
|
go sf.loop()
|
||||||
|
|
@ -253,40 +240,20 @@ func (sf *subfetcher) schedule(keys [][]byte) {
|
||||||
sf.lock.Lock()
|
sf.lock.Lock()
|
||||||
sf.tasks = append(sf.tasks, keys...)
|
sf.tasks = append(sf.tasks, keys...)
|
||||||
sf.lock.Unlock()
|
sf.lock.Unlock()
|
||||||
|
// Notify the prefetcher. The wake-chan is buffered, so this is async.
|
||||||
// Notify the prefetcher, it's fine if it's already terminated
|
sf.wake <- struct{}{}
|
||||||
select {
|
|
||||||
case sf.wake <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// peek tries to retrieve a deep copy of the fetcher's trie in whatever form it
|
// wait waits for the subfetcher to finish it's task. It is safe to call wait multiple
|
||||||
// is currently.
|
|
||||||
func (sf *subfetcher) peek() Trie {
|
|
||||||
ch := make(chan Trie)
|
|
||||||
select {
|
|
||||||
case sf.copy <- ch:
|
|
||||||
// Subfetcher still alive, return copy from it
|
|
||||||
return <-ch
|
|
||||||
|
|
||||||
case <-sf.term:
|
|
||||||
// Subfetcher already terminated, return a copy directly
|
|
||||||
if sf.trie == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return sf.db.CopyTrie(sf.trie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// abort interrupts the subfetcher immediately. It is safe to call abort multiple
|
|
||||||
// times but it is not thread safe.
|
// times but it is not thread safe.
|
||||||
func (sf *subfetcher) abort() {
|
func (sf *subfetcher) wait() {
|
||||||
select {
|
// Signal termination by nil tasks
|
||||||
case <-sf.stop:
|
sf.lock.Lock()
|
||||||
default:
|
sf.tasks = nil
|
||||||
close(sf.stop)
|
sf.lock.Unlock()
|
||||||
}
|
// Notify the prefetcher. The wake-chan is buffered, so this is async.
|
||||||
|
sf.wake <- struct{}{}
|
||||||
|
// Wait for it to terminate
|
||||||
<-sf.term
|
<-sf.term
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,33 +283,22 @@ func (sf *subfetcher) loop() {
|
||||||
}
|
}
|
||||||
// Trie opened successfully, keep prefetching items
|
// Trie opened successfully, keep prefetching items
|
||||||
for {
|
for {
|
||||||
select {
|
<-sf.wake
|
||||||
case <-sf.wake:
|
|
||||||
// Subfetcher was woken up, retrieve any tasks to avoid spinning the lock
|
// Subfetcher was woken up, retrieve any tasks to avoid spinning the lock
|
||||||
sf.lock.Lock()
|
sf.lock.Lock()
|
||||||
tasks := sf.tasks
|
tasks := sf.tasks
|
||||||
sf.tasks = nil
|
sf.tasks = nil
|
||||||
sf.lock.Unlock()
|
sf.lock.Unlock()
|
||||||
|
if tasks == nil {
|
||||||
// Prefetch any tasks until the loop is interrupted
|
// No more tasks
|
||||||
for i, task := range tasks {
|
|
||||||
select {
|
|
||||||
case <-sf.stop:
|
|
||||||
// If termination is requested, add any leftover back and return
|
|
||||||
sf.lock.Lock()
|
|
||||||
sf.tasks = append(sf.tasks, tasks[i:]...)
|
|
||||||
sf.lock.Unlock()
|
|
||||||
return
|
return
|
||||||
|
}
|
||||||
case ch := <-sf.copy:
|
// Prefetch all tasks
|
||||||
// Somebody wants a copy of the current trie, grant them
|
for _, task := range tasks {
|
||||||
ch <- sf.db.CopyTrie(sf.trie)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// No termination request yet, prefetch the next entry
|
|
||||||
if _, ok := sf.seen[string(task)]; ok {
|
if _, ok := sf.seen[string(task)]; ok {
|
||||||
sf.dups++
|
sf.dups++
|
||||||
} else {
|
continue
|
||||||
|
}
|
||||||
if len(task) == common.AddressLength {
|
if len(task) == common.AddressLength {
|
||||||
sf.trie.GetAccount(common.BytesToAddress(task))
|
sf.trie.GetAccount(common.BytesToAddress(task))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -351,15 +307,4 @@ func (sf *subfetcher) loop() {
|
||||||
sf.seen[string(task)] = struct{}{}
|
sf.seen[string(task)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case ch := <-sf.copy:
|
|
||||||
// Somebody wants a copy of the current trie, grant them
|
|
||||||
ch <- sf.db.CopyTrie(sf.trie)
|
|
||||||
|
|
||||||
case <-sf.stop:
|
|
||||||
// Termination is requested, abort and leave remaining tasks
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue