Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Benchmark contract state insertions with DB vs. DB transactions #1230

Merged
merged 22 commits into from
Jul 4, 2023

Conversation

bvrooman
Copy link
Contributor

@bvrooman bvrooman commented Jun 28, 2023

Related issues:

This PR adds additional benches to compare the performance of inserting state when the existing data is 1) written to the database, and 2) stored in-memory in a transaction. The results demonstrate that when preexisting data is held in a transaction, inserting state has constant complexity and is generally faster, and performance degradation comes from the logarithmic complexity of Merkle tree insertion.

insert state single contract database/insert state with 0 preexisting entries
                        time:   [3.0283 µs 3.0349 µs 3.0417 µs]
insert state single contract database/insert state with 1 preexisting entry
                        time:   [6.4704 µs 6.9960 µs 7.6233 µs]
insert state single contract database/insert state with 10 preexisting entries
                        time:   [15.750 µs 16.668 µs 17.636 µs]
insert state single contract database/insert state with 100 preexisting entries
                        time:   [29.102 µs 30.128 µs 31.204 µs]
insert state single contract database/insert state with 1,000 preexisting entries
                        time:   [45.264 µs 46.447 µs 47.666 µs]
insert state single contract database/insert state with 10,000 preexisting entries
                        time:   [63.839 µs 65.277 µs 66.784 µs]
insert state single contract database/insert state with 100,000 preexisting entries
                        time:   [85.863 µs 87.430 µs 89.119 µs]

insert state single contract transaction/insert state with 0 preexisting entries
                        time:   [2.4829 µs 2.8342 µs 3.4867 µs]
insert state single contract transaction/insert state with 1 preexisting entry
                        time:   [5.4261 µs 5.8455 µs 6.2946 µs]
insert state single contract transaction/insert state with 10 preexisting entries
                        time:   [11.104 µs 11.721 µs 12.352 µs]
insert state single contract transaction/insert state with 100 preexisting entries
                        time:   [20.425 µs 21.213 µs 21.987 µs]
insert state single contract transaction/insert state with 1,000 preexisting entries
                        time:   [28.998 µs 29.852 µs 30.714 µs]
insert state single contract transaction/insert state with 10,000 preexisting entries
                        time:   [38.645 µs 39.401 µs 40.206 µs]
insert state single contract transaction/insert state with 100,000 preexisting entries
                        time:   [49.969 µs 50.776 µs 51.611 µs]

insert state multiple contracts database/insert state with 0 preexisting entries
                        time:   [2.7846 µs 2.7901 µs 2.7962 µs]
insert state multiple contracts database/insert state with 1 preexisting entry
                        time:   [2.9599 µs 2.9688 µs 2.9782 µs]
insert state multiple contracts database/insert state with 10 preexisting entries
                        time:   [3.1822 µs 3.2080 µs 3.2350 µs]
insert state multiple contracts database/insert state with 100 preexisting entries
                        time:   [3.7841 µs 3.9514 µs 4.1838 µs]
insert state multiple contracts database/insert state with 1,000 preexisting entries
                        time:   [4.1301 µs 4.3894 µs 4.7190 µs]
insert state multiple contracts database/insert state with 10,000 preexisting entries
                        time:   [4.4221 µs 4.5124 µs 4.6034 µs]
insert state multiple contracts database/insert state with 100,000 preexisting entries
                        time:   [4.8611 µs 4.9762 µs 5.0904 µs]

insert state multiple contracts transaction/insert state with 0 preexisting entries
                        time:   [2.8071 µs 2.8121 µs 2.8175 µs]
insert state multiple contracts transaction/insert state with 1 preexisting entry
                        time:   [2.9778 µs 2.9842 µs 2.9912 µs]
insert state multiple contracts transaction/insert state with 10 preexisting entries
                        time:   [2.9502 µs 2.9587 µs 2.9677 µs]
insert state multiple contracts transaction/insert state with 100 preexisting entries
                        time:   [2.9706 µs 2.9779 µs 2.9854 µs]
insert state multiple contracts transaction/insert state with 1,000 preexisting entries
                        time:   [2.9824 µs 2.9910 µs 2.9999 µs]
insert state multiple contracts transaction/insert state with 10,000 preexisting entries
                        time:   [2.9799 µs 2.9887 µs 2.9985 µs]
insert state multiple contracts transaction/insert state with 100,000 preexisting entries
                        time:   [2.9731 µs 2.9846 µs 2.9963 µs]

@bvrooman bvrooman changed the base branch from master to bvrooman/chore/state-benchmarks June 28, 2023 10:52
@bvrooman bvrooman self-assigned this Jun 28, 2023
@bvrooman bvrooman changed the title chore: Benchmark contract state insertions with DB vs. DB transactions test: Benchmark contract state insertions with DB vs. DB transactions Jun 28, 2023
@xgreenx xgreenx marked this pull request as ready for review June 29, 2023 11:51
@xgreenx xgreenx marked this pull request as draft June 29, 2023 11:51
@bvrooman bvrooman marked this pull request as ready for review June 29, 2023 12:23
@bvrooman bvrooman marked this pull request as draft June 29, 2023 12:24
Base automatically changed from bvrooman/chore/state-benchmarks to master June 29, 2023 12:59
@bvrooman bvrooman requested a review from a team June 29, 2023 13:10
@bvrooman bvrooman marked this pull request as ready for review June 29, 2023 13:18
let mut elapsed_time = Duration::default();
for _ in 0..iters {
let mut inner = outer.transaction();
let mut inner_db = VmDatabase::new::<GeneratedConsensusFields>(
Copy link
Contributor Author

@bvrooman bvrooman Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally, I was exploring if I could implement From<DatabaseTransaction> for VmDatabase to simplify this logic. The implementation meant using the default for GeneratedConsensusFields, but that is only available under the test or test-helpers configuration. During benchmarking, neither of this flags are automatically available, and I did not think it would be valuable to require users to add a test configuration to their benchmarking at this time.

@Voxelot
Copy link
Member

Voxelot commented Jul 4, 2023

Hmm this seems strange, why is there such a big performance difference when there is a single contract vs multiple contract insert when using transactions?

insert state single contract transaction/insert state with 100,000 preexisting entries
                        time:   [49.969 µs 50.776 µs 51.611 µs]
..
insert state multiple contracts transaction/insert state with 100,000 preexisting entries
                        time:   [2.9731 µs 2.9846 µs 2.9963 µs]

@xgreenx
Copy link
Collaborator

xgreenx commented Jul 4, 2023

It is SMT. In the case of one contract with 100_000 entries, we must do many iterations over the tree.

Copy link
Collaborator

@xgreenx xgreenx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the benchmarks!=)

It seems the worst case is the insertion of a new state/balance into RocksDB with many entries.

We need to update our benchmark for all opcodes that insert something into the storage to include a huge pre-existing state. I think about 100_000_000 entries as a default value for benchmarks because after this value, the cost slows down, and it is almost impossible to have so many state/balance entries=)

@bvrooman
Copy link
Contributor Author

bvrooman commented Jul 4, 2023

Hmm this seems strange, why is there such a big performance difference when there is a single contract vs multiple contract insert when using transactions?

insert state single contract transaction/insert state with 100,000 preexisting entries
                        time:   [49.969 µs 50.776 µs 51.611 µs]
..
insert state multiple contracts transaction/insert state with 100,000 preexisting entries
                        time:   [2.9731 µs 2.9846 µs 2.9963 µs]

Yes, as Green mentioned, the performance difference comes from the different size of SMTs at the time of insertion. In the "single contract" test cases, we are inserting an entry for a contract with preexisting leaves (i.e. 100,000 leaves for the above test case) and accumulating the Merkle tree. The biggest cost here is the tree traversal and update. In the "multiple contract" test cases, we are inserting entries for 100,000 new contracts, each with their own Merkle tree. Because these trees are new, updating them is very fast.

@bvrooman bvrooman merged commit 45982da into master Jul 4, 2023
21 checks passed
@bvrooman bvrooman deleted the bvrooman/chore/state-benchmarks-db-transaction branch July 4, 2023 15:35
@xgreenx xgreenx mentioned this pull request Jul 16, 2023
xgreenx added a commit that referenced this pull request Jul 17, 2023
## Release v0.20.0

The release brings a couple of new breaking changes from the [`fuel-vm
0.35.0`](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.35.0) with
bugfixes. Check the description of the VM release for more details.

The `fuel-core` release mostly improved the internal codebase but also
brought some breaking changes:
- Removed `Trigger::Hybrid` PoA block trigger mode. Only
`Trigger::Instante` and `Trigger::Interval` are available for block
production now. The main mode for testnets and mainnet will be
`Interval`.
- Removed support for `OpaqueReceipt` and the `Receipt` type doesn't
have the `raw_payload` field anymore.
- A `Receipt` type got two new variants: `Mint` and `Burn`. The
corresponding opcodes emit these new events.
- The `AssetId` is derived from `ContractId` and additional nonce. So
the `ContractId` and `AssetId` can't be the same anymore.

## What's Changed
* bump rocksdb to enable compiling with GCC 13 by @segfault-magnet in
#1219
* setting peer reputation params by @leviathanbeak in
#1202
* Take into account the actually used gas by the transactions and fetch
more transaction by @xgreenx in
#1223
* Use production configuration for `fuel-core` during benches by
@xgreenx in #1227
* Speedup and stabilize unit and integration tests by @xgreenx in
#1231
* test: State benchmarks by @bvrooman in
#1226
* Remove hybrid PoA block trigger mode by @Dentosal in
#1232
* test: Benchmark contract state insertions with DB vs. DB transactions
by @bvrooman in #1230
* multiplatform docker builds by @Voxelot in
#1233
* Fix typo in architecture.md by @eltociear in
#1241
* Expose gas cost in chain info by @MitchTurner in
#1244
* Reuse calculated tx id in executor by @MitchTurner in
#1248
* Fix multi-platform images by @Voxelot in
#1251
* Add logging of the long GraphQL queries for future debug by
@MitchTurner in #1250
* Reused `CheckedTransaction` from transaction pool in the executor by
@xgreenx in #1249
* Bump `fuel-vm` to `0.35.0` version by @xgreenx in
#1256

## New Contributors
* @segfault-magnet made their first contribution in
#1219
* @eltociear made their first contribution in
#1241
* @MitchTurner made their first contribution in
#1244

**Full Changelog**:
v0.19.1...v0.20.0
GoldenPath1109 added a commit to GoldenPath1109/fuel-core that referenced this pull request Sep 7, 2024
## Release v0.20.0

The release brings a couple of new breaking changes from the [`fuel-vm
0.35.0`](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.35.0) with
bugfixes. Check the description of the VM release for more details.

The `fuel-core` release mostly improved the internal codebase but also
brought some breaking changes:
- Removed `Trigger::Hybrid` PoA block trigger mode. Only
`Trigger::Instante` and `Trigger::Interval` are available for block
production now. The main mode for testnets and mainnet will be
`Interval`.
- Removed support for `OpaqueReceipt` and the `Receipt` type doesn't
have the `raw_payload` field anymore.
- A `Receipt` type got two new variants: `Mint` and `Burn`. The
corresponding opcodes emit these new events.
- The `AssetId` is derived from `ContractId` and additional nonce. So
the `ContractId` and `AssetId` can't be the same anymore.

## What's Changed
* bump rocksdb to enable compiling with GCC 13 by @segfault-magnet in
FuelLabs/fuel-core#1219
* setting peer reputation params by @leviathanbeak in
FuelLabs/fuel-core#1202
* Take into account the actually used gas by the transactions and fetch
more transaction by @xgreenx in
FuelLabs/fuel-core#1223
* Use production configuration for `fuel-core` during benches by
@xgreenx in FuelLabs/fuel-core#1227
* Speedup and stabilize unit and integration tests by @xgreenx in
FuelLabs/fuel-core#1231
* test: State benchmarks by @bvrooman in
FuelLabs/fuel-core#1226
* Remove hybrid PoA block trigger mode by @Dentosal in
FuelLabs/fuel-core#1232
* test: Benchmark contract state insertions with DB vs. DB transactions
by @bvrooman in FuelLabs/fuel-core#1230
* multiplatform docker builds by @Voxelot in
FuelLabs/fuel-core#1233
* Fix typo in architecture.md by @eltociear in
FuelLabs/fuel-core#1241
* Expose gas cost in chain info by @MitchTurner in
FuelLabs/fuel-core#1244
* Reuse calculated tx id in executor by @MitchTurner in
FuelLabs/fuel-core#1248
* Fix multi-platform images by @Voxelot in
FuelLabs/fuel-core#1251
* Add logging of the long GraphQL queries for future debug by
@MitchTurner in FuelLabs/fuel-core#1250
* Reused `CheckedTransaction` from transaction pool in the executor by
@xgreenx in FuelLabs/fuel-core#1249
* Bump `fuel-vm` to `0.35.0` version by @xgreenx in
FuelLabs/fuel-core#1256

## New Contributors
* @segfault-magnet made their first contribution in
FuelLabs/fuel-core#1219
* @eltociear made their first contribution in
FuelLabs/fuel-core#1241
* @MitchTurner made their first contribution in
FuelLabs/fuel-core#1244

**Full Changelog**:
FuelLabs/fuel-core@v0.19.1...v0.20.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

State performance tests
3 participants