@@ -44,31 +44,49 @@ type triePrefetcher struct {
4444 root common.Hash // Root hash of the account trie for metrics
4545 fetchers map [string ]* subfetcher // Subfetchers for each trie
4646 term chan struct {} // Channel to signal interruption
47+ noreads bool // Whether to ignore state-read-only prefetch requests
4748
4849 deliveryMissMeter metrics.Meter
49- accountLoadMeter metrics.Meter
50- accountDupMeter metrics.Meter
51- accountWasteMeter metrics.Meter
52- storageLoadMeter metrics.Meter
53- storageDupMeter metrics.Meter
54- storageWasteMeter metrics.Meter
50+
51+ accountLoadReadMeter metrics.Meter
52+ accountLoadWriteMeter metrics.Meter
53+ accountDupReadMeter metrics.Meter
54+ accountDupWriteMeter metrics.Meter
55+ accountDupCrossMeter metrics.Meter
56+ accountWasteMeter metrics.Meter
57+
58+ storageLoadReadMeter metrics.Meter
59+ storageLoadWriteMeter metrics.Meter
60+ storageDupReadMeter metrics.Meter
61+ storageDupWriteMeter metrics.Meter
62+ storageDupCrossMeter metrics.Meter
63+ storageWasteMeter metrics.Meter
5564}
5665
57- func newTriePrefetcher (db Database , root common.Hash , namespace string ) * triePrefetcher {
66+ func newTriePrefetcher (db Database , root common.Hash , namespace string , noreads bool ) * triePrefetcher {
5867 prefix := triePrefetchMetricsPrefix + namespace
5968 return & triePrefetcher {
6069 db : db ,
6170 root : root ,
6271 fetchers : make (map [string ]* subfetcher ), // Active prefetchers use the fetchers map
6372 term : make (chan struct {}),
73+ noreads : noreads ,
6474
6575 deliveryMissMeter : metrics .GetOrRegisterMeter (prefix + "/deliverymiss" , nil ),
66- accountLoadMeter : metrics .GetOrRegisterMeter (prefix + "/account/load" , nil ),
67- accountDupMeter : metrics .GetOrRegisterMeter (prefix + "/account/dup" , nil ),
68- accountWasteMeter : metrics .GetOrRegisterMeter (prefix + "/account/waste" , nil ),
69- storageLoadMeter : metrics .GetOrRegisterMeter (prefix + "/storage/load" , nil ),
70- storageDupMeter : metrics .GetOrRegisterMeter (prefix + "/storage/dup" , nil ),
71- storageWasteMeter : metrics .GetOrRegisterMeter (prefix + "/storage/waste" , nil ),
76+
77+ accountLoadReadMeter : metrics .GetOrRegisterMeter (prefix + "/account/load/read" , nil ),
78+ accountLoadWriteMeter : metrics .GetOrRegisterMeter (prefix + "/account/load/write" , nil ),
79+ accountDupReadMeter : metrics .GetOrRegisterMeter (prefix + "/account/dup/read" , nil ),
80+ accountDupWriteMeter : metrics .GetOrRegisterMeter (prefix + "/account/dup/write" , nil ),
81+ accountDupCrossMeter : metrics .GetOrRegisterMeter (prefix + "/account/dup/cross" , nil ),
82+ accountWasteMeter : metrics .GetOrRegisterMeter (prefix + "/account/waste" , nil ),
83+
84+ storageLoadReadMeter : metrics .GetOrRegisterMeter (prefix + "/storage/load/read" , nil ),
85+ storageLoadWriteMeter : metrics .GetOrRegisterMeter (prefix + "/storage/load/write" , nil ),
86+ storageDupReadMeter : metrics .GetOrRegisterMeter (prefix + "/storage/dup/read" , nil ),
87+ storageDupWriteMeter : metrics .GetOrRegisterMeter (prefix + "/storage/dup/write" , nil ),
88+ storageDupCrossMeter : metrics .GetOrRegisterMeter (prefix + "/storage/dup/cross" , nil ),
89+ storageWasteMeter : metrics .GetOrRegisterMeter (prefix + "/storage/waste" , nil ),
7290 }
7391}
7492
@@ -98,19 +116,31 @@ func (p *triePrefetcher) report() {
98116 fetcher .wait () // ensure the fetcher's idle before poking in its internals
99117
100118 if fetcher .root == p .root {
101- p .accountLoadMeter .Mark (int64 (len (fetcher .seen )))
102- p .accountDupMeter .Mark (int64 (fetcher .dups ))
119+ p .accountLoadReadMeter .Mark (int64 (len (fetcher .seenRead )))
120+ p .accountLoadWriteMeter .Mark (int64 (len (fetcher .seenWrite )))
121+
122+ p .accountDupReadMeter .Mark (int64 (fetcher .dupsRead ))
123+ p .accountDupWriteMeter .Mark (int64 (fetcher .dupsWrite ))
124+ p .accountDupCrossMeter .Mark (int64 (fetcher .dupsCross ))
125+
103126 for _ , key := range fetcher .used {
104- delete (fetcher .seen , string (key ))
127+ delete (fetcher .seenRead , string (key ))
128+ delete (fetcher .seenWrite , string (key ))
105129 }
106- p .accountWasteMeter .Mark (int64 (len (fetcher .seen )))
130+ p .accountWasteMeter .Mark (int64 (len (fetcher .seenRead ) + len ( fetcher . seenWrite )))
107131 } else {
108- p .storageLoadMeter .Mark (int64 (len (fetcher .seen )))
109- p .storageDupMeter .Mark (int64 (fetcher .dups ))
132+ p .storageLoadReadMeter .Mark (int64 (len (fetcher .seenRead )))
133+ p .storageLoadWriteMeter .Mark (int64 (len (fetcher .seenWrite )))
134+
135+ p .storageDupReadMeter .Mark (int64 (fetcher .dupsRead ))
136+ p .storageDupWriteMeter .Mark (int64 (fetcher .dupsWrite ))
137+ p .storageDupCrossMeter .Mark (int64 (fetcher .dupsCross ))
138+
110139 for _ , key := range fetcher .used {
111- delete (fetcher .seen , string (key ))
140+ delete (fetcher .seenRead , string (key ))
141+ delete (fetcher .seenWrite , string (key ))
112142 }
113- p .storageWasteMeter .Mark (int64 (len (fetcher .seen )))
143+ p .storageWasteMeter .Mark (int64 (len (fetcher .seenRead ) + len ( fetcher . seenWrite )))
114144 }
115145 }
116146}
@@ -126,7 +156,11 @@ func (p *triePrefetcher) report() {
126156// upon the same contract, the parameters invoking this method may be
127157// repeated.
128158// 2. Finalize of the main account trie. This happens only once per block.
129- func (p * triePrefetcher ) prefetch (owner common.Hash , root common.Hash , addr common.Address , keys [][]byte ) error {
159+ func (p * triePrefetcher ) prefetch (owner common.Hash , root common.Hash , addr common.Address , keys [][]byte , read bool ) error {
160+ // If the state item is only being read, but reads are disabled, return
161+ if read && p .noreads {
162+ return nil
163+ }
130164 // Ensure the subfetcher is still alive
131165 select {
132166 case <- p .term :
@@ -139,7 +173,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr comm
139173 fetcher = newSubfetcher (p .db , p .root , owner , root , addr )
140174 p .fetchers [id ] = fetcher
141175 }
142- return fetcher .schedule (keys )
176+ return fetcher .schedule (keys , read )
143177}
144178
145179// trie returns the trie matching the root hash, blocking until the fetcher of
@@ -186,38 +220,51 @@ type subfetcher struct {
186220 addr common.Address // Address of the account that the trie belongs to
187221 trie Trie // Trie being populated with nodes
188222
189- tasks [][] byte // Items queued up for retrieval
190- lock sync.Mutex // Lock protecting the task queue
223+ tasks []* subfetcherTask // Items queued up for retrieval
224+ lock sync.Mutex // Lock protecting the task queue
191225
192226 wake chan struct {} // Wake channel if a new task is scheduled
193227 stop chan struct {} // Channel to interrupt processing
194228 term chan struct {} // Channel to signal interruption
195229
196- seen map [string ]struct {} // Tracks the entries already loaded
197- dups int // Number of duplicate preload tasks
198- used [][]byte // Tracks the entries used in the end
230+ seenRead map [string ]struct {} // Tracks the entries already loaded via read operations
231+ seenWrite map [string ]struct {} // Tracks the entries already loaded via write operations
232+
233+ dupsRead int // Number of duplicate preload tasks via reads only
234+ dupsWrite int // Number of duplicate preload tasks via writes only
235+ dupsCross int // Number of duplicate preload tasks via read-write-crosses
236+
237+ used [][]byte // Tracks the entries used in the end
238+ }
239+
240+ // subfetcherTask is a trie path to prefetch, tagged with whether it originates
241+ // from a read or a write request.
242+ type subfetcherTask struct {
243+ read bool
244+ key []byte
199245}
200246
201247// newSubfetcher creates a goroutine to prefetch state items belonging to a
202248// particular root hash.
203249func newSubfetcher (db Database , state common.Hash , owner common.Hash , root common.Hash , addr common.Address ) * subfetcher {
204250 sf := & subfetcher {
205- db : db ,
206- state : state ,
207- owner : owner ,
208- root : root ,
209- addr : addr ,
210- wake : make (chan struct {}, 1 ),
211- stop : make (chan struct {}),
212- term : make (chan struct {}),
213- seen : make (map [string ]struct {}),
251+ db : db ,
252+ state : state ,
253+ owner : owner ,
254+ root : root ,
255+ addr : addr ,
256+ wake : make (chan struct {}, 1 ),
257+ stop : make (chan struct {}),
258+ term : make (chan struct {}),
259+ seenRead : make (map [string ]struct {}),
260+ seenWrite : make (map [string ]struct {}),
214261 }
215262 go sf .loop ()
216263 return sf
217264}
218265
219266// schedule adds a batch of trie keys to the queue to prefetch.
220- func (sf * subfetcher ) schedule (keys [][]byte ) error {
267+ func (sf * subfetcher ) schedule (keys [][]byte , read bool ) error {
221268 // Ensure the subfetcher is still alive
222269 select {
223270 case <- sf .term :
@@ -226,7 +273,10 @@ func (sf *subfetcher) schedule(keys [][]byte) error {
226273 }
227274 // Append the tasks to the current queue
228275 sf .lock .Lock ()
229- sf .tasks = append (sf .tasks , keys ... )
276+ for _ , key := range keys {
277+ key := key // closure for the append below
278+ sf .tasks = append (sf .tasks , & subfetcherTask {read : read , key : key })
279+ }
230280 sf .lock .Unlock ()
231281
232282 // Notify the background thread to execute scheduled tasks
@@ -303,16 +353,36 @@ func (sf *subfetcher) loop() {
303353 sf .lock .Unlock ()
304354
305355 for _ , task := range tasks {
306- if _ , ok := sf .seen [string (task )]; ok {
307- sf .dups ++
308- continue
356+ key := string (task .key )
357+ if task .read {
358+ if _ , ok := sf .seenRead [key ]; ok {
359+ sf .dupsRead ++
360+ continue
361+ }
362+ if _ , ok := sf .seenWrite [key ]; ok {
363+ sf .dupsCross ++
364+ continue
365+ }
366+ } else {
367+ if _ , ok := sf .seenRead [key ]; ok {
368+ sf .dupsCross ++
369+ continue
370+ }
371+ if _ , ok := sf .seenWrite [key ]; ok {
372+ sf .dupsWrite ++
373+ continue
374+ }
375+ }
376+ if len (task .key ) == common .AddressLength {
377+ sf .trie .GetAccount (common .BytesToAddress (task .key ))
378+ } else {
379+ sf .trie .GetStorage (sf .addr , task .key )
309380 }
310- if len ( task ) == common . AddressLength {
311- sf .trie . GetAccount ( common . BytesToAddress ( task ))
381+ if task . read {
382+ sf .seenRead [ key ] = struct {}{}
312383 } else {
313- sf .trie . GetStorage ( sf . addr , task )
384+ sf .seenWrite [ key ] = struct {}{}
314385 }
315- sf .seen [string (task )] = struct {}{}
316386 }
317387
318388 case <- sf .stop :
0 commit comments