difflayer_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package snapshot
  17. import (
  18. "bytes"
  19. "math/big"
  20. "math/rand"
  21. "os"
  22. "path"
  23. "testing"
  24. "github.com/VictoriaMetrics/fastcache"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/ethdb/memorydb"
  27. "github.com/ethereum/go-ethereum/rlp"
  28. )
  29. func randomAccount() []byte {
  30. root := randomHash()
  31. a := Account{
  32. Balance: big.NewInt(rand.Int63()),
  33. Nonce: rand.Uint64(),
  34. Root: root[:],
  35. CodeHash: emptyCode[:],
  36. }
  37. data, _ := rlp.EncodeToBytes(a)
  38. return data
  39. }
  40. // TestMergeBasics tests some simple merges
  41. func TestMergeBasics(t *testing.T) {
  42. var (
  43. accounts = make(map[common.Hash][]byte)
  44. storage = make(map[common.Hash]map[common.Hash][]byte)
  45. )
  46. // Fill up a parent
  47. for i := 0; i < 100; i++ {
  48. h := randomHash()
  49. data := randomAccount()
  50. accounts[h] = data
  51. if rand.Intn(20) < 10 {
  52. accStorage := make(map[common.Hash][]byte)
  53. value := make([]byte, 32)
  54. rand.Read(value)
  55. accStorage[randomHash()] = value
  56. storage[h] = accStorage
  57. }
  58. }
  59. // Add some (identical) layers on top
  60. parent := newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage)
  61. child := newDiffLayer(parent, common.Hash{}, accounts, storage)
  62. child = newDiffLayer(child, common.Hash{}, accounts, storage)
  63. child = newDiffLayer(child, common.Hash{}, accounts, storage)
  64. child = newDiffLayer(child, common.Hash{}, accounts, storage)
  65. // And flatten
  66. merged := (child.flatten()).(*diffLayer)
  67. { // Check account lists
  68. // Should be zero/nil first
  69. if got, exp := len(merged.accountList), 0; got != exp {
  70. t.Errorf("accountList wrong, got %v exp %v", got, exp)
  71. }
  72. // Then set when we call AccountList
  73. if got, exp := len(merged.AccountList()), len(accounts); got != exp {
  74. t.Errorf("AccountList() wrong, got %v exp %v", got, exp)
  75. }
  76. if got, exp := len(merged.accountList), len(accounts); got != exp {
  77. t.Errorf("accountList [2] wrong, got %v exp %v", got, exp)
  78. }
  79. }
  80. { // Check storage lists
  81. i := 0
  82. for aHash, sMap := range storage {
  83. if got, exp := len(merged.storageList), i; got != exp {
  84. t.Errorf("[1] storageList wrong, got %v exp %v", got, exp)
  85. }
  86. if got, exp := len(merged.StorageList(aHash)), len(sMap); got != exp {
  87. t.Errorf("[2] StorageList() wrong, got %v exp %v", got, exp)
  88. }
  89. if got, exp := len(merged.storageList[aHash]), len(sMap); got != exp {
  90. t.Errorf("storageList wrong, got %v exp %v", got, exp)
  91. }
  92. i++
  93. }
  94. }
  95. }
  96. // TestMergeDelete tests some deletion
  97. func TestMergeDelete(t *testing.T) {
  98. var (
  99. storage = make(map[common.Hash]map[common.Hash][]byte)
  100. )
  101. // Fill up a parent
  102. h1 := common.HexToHash("0x01")
  103. h2 := common.HexToHash("0x02")
  104. flip := func() map[common.Hash][]byte {
  105. accs := make(map[common.Hash][]byte)
  106. accs[h1] = randomAccount()
  107. accs[h2] = nil
  108. return accs
  109. }
  110. flop := func() map[common.Hash][]byte {
  111. accs := make(map[common.Hash][]byte)
  112. accs[h1] = nil
  113. accs[h2] = randomAccount()
  114. return accs
  115. }
  116. // Add some flip-flopping layers on top
  117. parent := newDiffLayer(emptyLayer(), common.Hash{}, flip(), storage)
  118. child := parent.Update(common.Hash{}, flop(), storage)
  119. child = child.Update(common.Hash{}, flip(), storage)
  120. child = child.Update(common.Hash{}, flop(), storage)
  121. child = child.Update(common.Hash{}, flip(), storage)
  122. child = child.Update(common.Hash{}, flop(), storage)
  123. child = child.Update(common.Hash{}, flip(), storage)
  124. if data, _ := child.Account(h1); data == nil {
  125. t.Errorf("last diff layer: expected %x to be non-nil", h1)
  126. }
  127. if data, _ := child.Account(h2); data != nil {
  128. t.Errorf("last diff layer: expected %x to be nil", h2)
  129. }
  130. // And flatten
  131. merged := (child.flatten()).(*diffLayer)
  132. if data, _ := merged.Account(h1); data == nil {
  133. t.Errorf("merged layer: expected %x to be non-nil", h1)
  134. }
  135. if data, _ := merged.Account(h2); data != nil {
  136. t.Errorf("merged layer: expected %x to be nil", h2)
  137. }
  138. // If we add more granular metering of memory, we can enable this again,
  139. // but it's not implemented for now
  140. //if got, exp := merged.memory, child.memory; got != exp {
  141. // t.Errorf("mem wrong, got %d, exp %d", got, exp)
  142. //}
  143. }
  144. // This tests that if we create a new account, and set a slot, and then merge
  145. // it, the lists will be correct.
  146. func TestInsertAndMerge(t *testing.T) {
  147. // Fill up a parent
  148. var (
  149. acc = common.HexToHash("0x01")
  150. slot = common.HexToHash("0x02")
  151. parent *diffLayer
  152. child *diffLayer
  153. )
  154. {
  155. var accounts = make(map[common.Hash][]byte)
  156. var storage = make(map[common.Hash]map[common.Hash][]byte)
  157. parent = newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage)
  158. }
  159. {
  160. var accounts = make(map[common.Hash][]byte)
  161. var storage = make(map[common.Hash]map[common.Hash][]byte)
  162. accounts[acc] = randomAccount()
  163. accstorage := make(map[common.Hash][]byte)
  164. storage[acc] = accstorage
  165. storage[acc][slot] = []byte{0x01}
  166. child = newDiffLayer(parent, common.Hash{}, accounts, storage)
  167. }
  168. // And flatten
  169. merged := (child.flatten()).(*diffLayer)
  170. { // Check that slot value is present
  171. got, _ := merged.Storage(acc, slot)
  172. if exp := []byte{0x01}; bytes.Compare(got, exp) != 0 {
  173. t.Errorf("merged slot value wrong, got %x, exp %x", got, exp)
  174. }
  175. }
  176. }
  177. func emptyLayer() *diskLayer {
  178. return &diskLayer{
  179. diskdb: memorydb.New(),
  180. cache: fastcache.New(500 * 1024),
  181. }
  182. }
  183. // BenchmarkSearch checks how long it takes to find a non-existing key
  184. // BenchmarkSearch-6 200000 10481 ns/op (1K per layer)
  185. // BenchmarkSearch-6 200000 10760 ns/op (10K per layer)
  186. // BenchmarkSearch-6 100000 17866 ns/op
  187. //
  188. // BenchmarkSearch-6 500000 3723 ns/op (10k per layer, only top-level RLock()
  189. func BenchmarkSearch(b *testing.B) {
  190. // First, we set up 128 diff layers, with 1K items each
  191. fill := func(parent snapshot) *diffLayer {
  192. accounts := make(map[common.Hash][]byte)
  193. storage := make(map[common.Hash]map[common.Hash][]byte)
  194. for i := 0; i < 10000; i++ {
  195. accounts[randomHash()] = randomAccount()
  196. }
  197. return newDiffLayer(parent, common.Hash{}, accounts, storage)
  198. }
  199. var layer snapshot
  200. layer = emptyLayer()
  201. for i := 0; i < 128; i++ {
  202. layer = fill(layer)
  203. }
  204. key := common.Hash{}
  205. b.ResetTimer()
  206. for i := 0; i < b.N; i++ {
  207. layer.AccountRLP(key)
  208. }
  209. }
  210. // BenchmarkSearchSlot checks how long it takes to find a non-existing key
  211. // - Number of layers: 128
  212. // - Each layers contains the account, with a couple of storage slots
  213. // BenchmarkSearchSlot-6 100000 14554 ns/op
  214. // BenchmarkSearchSlot-6 100000 22254 ns/op (when checking parent root using mutex)
  215. // BenchmarkSearchSlot-6 100000 14551 ns/op (when checking parent number using atomic)
  216. func BenchmarkSearchSlot(b *testing.B) {
  217. // First, we set up 128 diff layers, with 1K items each
  218. accountKey := common.Hash{}
  219. storageKey := common.HexToHash("0x1337")
  220. accountRLP := randomAccount()
  221. fill := func(parent snapshot) *diffLayer {
  222. accounts := make(map[common.Hash][]byte)
  223. accounts[accountKey] = accountRLP
  224. storage := make(map[common.Hash]map[common.Hash][]byte)
  225. accStorage := make(map[common.Hash][]byte)
  226. for i := 0; i < 5; i++ {
  227. value := make([]byte, 32)
  228. rand.Read(value)
  229. accStorage[randomHash()] = value
  230. storage[accountKey] = accStorage
  231. }
  232. return newDiffLayer(parent, common.Hash{}, accounts, storage)
  233. }
  234. var layer snapshot
  235. layer = emptyLayer()
  236. for i := 0; i < 128; i++ {
  237. layer = fill(layer)
  238. }
  239. b.ResetTimer()
  240. for i := 0; i < b.N; i++ {
  241. layer.Storage(accountKey, storageKey)
  242. }
  243. }
  244. // With accountList and sorting
  245. //BenchmarkFlatten-6 50 29890856 ns/op
  246. //
  247. // Without sorting and tracking accountlist
  248. // BenchmarkFlatten-6 300 5511511 ns/op
  249. func BenchmarkFlatten(b *testing.B) {
  250. fill := func(parent snapshot) *diffLayer {
  251. accounts := make(map[common.Hash][]byte)
  252. storage := make(map[common.Hash]map[common.Hash][]byte)
  253. for i := 0; i < 100; i++ {
  254. accountKey := randomHash()
  255. accounts[accountKey] = randomAccount()
  256. accStorage := make(map[common.Hash][]byte)
  257. for i := 0; i < 20; i++ {
  258. value := make([]byte, 32)
  259. rand.Read(value)
  260. accStorage[randomHash()] = value
  261. }
  262. storage[accountKey] = accStorage
  263. }
  264. return newDiffLayer(parent, common.Hash{}, accounts, storage)
  265. }
  266. b.ResetTimer()
  267. for i := 0; i < b.N; i++ {
  268. b.StopTimer()
  269. var layer snapshot
  270. layer = emptyLayer()
  271. for i := 1; i < 128; i++ {
  272. layer = fill(layer)
  273. }
  274. b.StartTimer()
  275. for i := 1; i < 128; i++ {
  276. dl, ok := layer.(*diffLayer)
  277. if !ok {
  278. break
  279. }
  280. layer = dl.flatten()
  281. }
  282. b.StopTimer()
  283. }
  284. }
  285. // This test writes ~324M of diff layers to disk, spread over
  286. // - 128 individual layers,
  287. // - each with 200 accounts
  288. // - containing 200 slots
  289. //
  290. // BenchmarkJournal-6 1 1471373923 ns/ops
  291. // BenchmarkJournal-6 1 1208083335 ns/op // bufio writer
  292. func BenchmarkJournal(b *testing.B) {
  293. fill := func(parent snapshot) *diffLayer {
  294. accounts := make(map[common.Hash][]byte)
  295. storage := make(map[common.Hash]map[common.Hash][]byte)
  296. for i := 0; i < 200; i++ {
  297. accountKey := randomHash()
  298. accounts[accountKey] = randomAccount()
  299. accStorage := make(map[common.Hash][]byte)
  300. for i := 0; i < 200; i++ {
  301. value := make([]byte, 32)
  302. rand.Read(value)
  303. accStorage[randomHash()] = value
  304. }
  305. storage[accountKey] = accStorage
  306. }
  307. return newDiffLayer(parent, common.Hash{}, accounts, storage)
  308. }
  309. layer := snapshot(new(diskLayer))
  310. for i := 1; i < 128; i++ {
  311. layer = fill(layer)
  312. }
  313. b.ResetTimer()
  314. for i := 0; i < b.N; i++ {
  315. f, _, _ := layer.Journal(path.Join(os.TempDir(), "difflayer_journal.tmp"))
  316. f.Close()
  317. }
  318. }