neoneo-go/pkg/core
Roman Khimov b9be892bf9 storage: allow accessing MemCachedStore during Persist
Persist by its definition doesn't change MemCachedStore visible state, all KV
pairs that were acessible via it before Persist remain accessible after
Persist. The only thing it does is flushing of the current set of KV pairs
from memory to peristent store. To do that it needs read-only access to the
current KV pair set, but technically it then replaces maps, so we have to use
full write lock which makes MemCachedStore inaccessible for the duration of
Persist. And Persist can take a lot of time, it's about disk access for
regular DBs.

What we do here is we create new in-memory maps for MemCachedStore before
flushing old ones to the persistent store. Then a fake persistent store is
created which actually is a MemCachedStore with old maps, so it has exactly
the same visible state. This Store is never accessed for writes, so we can
read it without taking any internal locks and at the same time we no longer
need write locks for original MemCachedStore, we're not using it. All of this
makes it possible to use MemCachedStore as normally reads are handled going
down to whatever level is needed and writes are handled by new maps. So while
Persist for (*Blockchain).dao does its most time-consuming work we can process
other blocks (reading data for transactions and persisting storeBlock caches
to (*Blockchain).dao).

The change was tested for performance with neo-bench (single node, 10 workers,
LevelDB) on two machines and block dump processing (RC4 testnet up to 62800
with VerifyBlocks set to false) on i7-8565U.

Reference results (bbe4e9cd7b):

Ryzen 9 5950X:
RPS     23616.969 22817.086 23222.378  ≈ 23218   ± 1.72%
TPS     23047.316 22608.578 22735.540  ≈ 22797   ± 0.99%
CPU %      23.434    25.553    23.848  ≈    24.3 ± 4.63%
Mem MB    600.636   503.060   582.043  ≈   562   ± 9.22%

Core i7-8565U:
RPS     6594.007 6499.501 6572.902  ≈ 6555   ± 0.76%
TPS     6561.680 6444.545 6510.120  ≈ 6505   ± 0.90%
CPU %     58.452   60.568   62.474    ≈ 60.5 ± 3.33%
Mem MB   234.893  285.067  269.081   ≈ 263   ± 9.75%

DB restore:
real    0m22.237s 0m23.471s 0m23.409s  ≈ 23.04 ± 3.02%
user    0m35.435s 0m38.943s 0m39.247s  ≈ 37.88 ± 5.59%
sys      0m3.085s  0m3.360s  0m3.144s  ≈  3.20 ± 4.53%

After the change:

Ryzen 9 5950X:
RPS     27747.349 27407.726 27520.210  ≈ 27558   ± 0.63%  ↑ 18.69%
TPS     26992.010 26993.468 27010.966  ≈ 26999   ± 0.04%  ↑ 18.43%
CPU %      28.928    28.096    29.105  ≈    28.7 ± 1.88%  ↑ 18.1%
Mem MB    760.385   726.320   756.118  ≈   748   ± 2.48%  ↑ 33.10%

Core i7-8565U:
RPS     7783.229 7628.409 7542.340  ≈ 7651   ± 1.60%  ↑ 16.72%
TPS     7708.436 7607.397 7489.459  ≈ 7602   ± 1.44%  ↑ 16.85%
CPU %     74.899   71.020   72.697  ≈   72.9 ± 2.67%  ↑ 20.50%
Mem MB   438.047  436.967  416.350  ≈  430   ± 2.84%  ↑ 63.50%

DB restore:
real    0m20.838s 0m21.895s 0m21.794s  ≈ 21.51 ± 2.71%  ↓ 6.64%
user    0m39.091s 0m40.565s 0m41.493s  ≈ 40.38 ± 3.00%  ↑ 6.60%
sys      0m3.184s  0m2.923s  0m3.062s  ≈  3.06 ± 4.27%  ↓ 4.38%

It obviously uses more memory now and utilizes CPU more aggressively, but at
the same time it allows to improve all relevant metrics and finally reach a
situation where we process 50K transactions in less than second on Ryzen 9
5950X (going higher than 25K TPS). The other observation is much more stable
block time, on Ryzen 9 it's as close to 1 second as it could be.
2021-08-02 16:33:00 +03:00
..
block core/block: add Nonce field to header 2021-07-15 15:58:49 +03:00
blockchainer core: implement dynamic NEP17 balances tracking 2021-07-29 10:23:01 +03:00
chaindump block: drop Network from the Header 2021-03-26 13:45:18 +03:00
dao dao: drop dropNEP17Cache from Cached 2021-07-30 15:45:17 +03:00
fee fee: adjust SQRT price 2021-07-19 15:42:41 +03:00
interop interop: use non-Cached wrapped DAO 2021-07-30 15:45:17 +03:00
mempool core: move mempool.Event to a separate package 2021-06-01 12:24:28 +03:00
mempoolevent core: move mempool.Event to a separate package 2021-06-01 12:24:28 +03:00
mpt slice: introduce common Copy helper 2021-07-19 22:57:55 +03:00
native interop: use non-Cached wrapped DAO 2021-07-30 15:45:17 +03:00
state core: implement dynamic NEP17 balances tracking 2021-07-29 10:23:01 +03:00
stateroot *: increase GAS for verification 2021-07-14 10:27:09 +03:00
storage storage: allow accessing MemCachedStore during Persist 2021-08-02 16:33:00 +03:00
test_data core/block: add Nonce field to header 2021-07-15 15:58:49 +03:00
transaction oracle: check response Content-Type 2021-07-12 13:13:48 +03:00
blockchain.go core: spread storeBlock actions to three goroutines 2021-07-30 15:45:17 +03:00
blockchain_test.go config: add InitialGASSupply, fix #2073 2021-07-20 16:59:54 +03:00
doc.go core: add Blockchain event subscription mechanism 2020-05-25 00:27:39 +03:00
helper_test.go core: implement dynamic NEP17 balances tracking 2021-07-29 10:23:01 +03:00
interop_system.go *: simplify some integer checks with IsUint64() 2021-07-19 15:42:42 +03:00
interop_system_test.go config: update mainnet magic 2021-07-21 14:42:26 +03:00
interops.go interop: implement System.Runtime.GetRandom 2021-07-15 16:00:01 +03:00
interops_test.go dao: drop network from DAO 2021-03-26 13:45:18 +03:00
native_contract_test.go dao: drop network from DAO 2021-03-26 13:45:18 +03:00
native_designate_test.go core/test: get rid of empty tx scripts 2021-07-15 15:58:49 +03:00
native_gas_test.go core: implement dynamic NEP17 balances tracking 2021-07-29 10:23:01 +03:00
native_ledger_test.go core/block: add Nonce field to header 2021-07-15 15:58:49 +03:00
native_management_test.go core: maintain a set of NEP17-compliant contracts 2021-07-28 13:22:53 +03:00
native_neo_test.go core/test: get rid of empty tx scripts 2021-07-15 15:58:49 +03:00
native_notary_test.go config: make MaxValidUntilBlockIncrement configurable 2021-05-17 13:43:03 +03:00
native_oracle_test.go core/test: get rid of empty tx scripts 2021-07-15 15:58:49 +03:00
native_policy_test.go native: allow to set candidate register price 2021-03-11 10:12:30 +03:00
nonnative_name_service_test.go examples: fix IPv6 bounds check 2021-05-28 11:31:09 +03:00
notary_test.go notary: process new transactions in a separate goroutine 2021-07-23 14:48:00 +03:00
oracle_test.go *: simplify some error messages 2021-07-23 10:08:09 +03:00
prometheus.go stateroot: move state-root related logic to core/stateroot 2021-03-09 13:48:29 +03:00
stateroot_test.go *: create real temporary dirs and files in tests 2021-07-20 12:51:11 +03:00
util.go core/block: add Nonce field to header 2021-07-15 15:58:49 +03:00
util_test.go core/block: add Nonce field to header 2021-07-15 15:58:49 +03:00