├── .github ├── FUNDING.yml └── workflows │ └── report.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bench.sh ├── bench ├── alien_signals.md ├── alien_signals_v0.3.md ├── mobx.md ├── preact_signals.md ├── riverpod.md ├── signals_core.md ├── solidart.md ├── solidart_v1.md └── state_beacon.md ├── frameworks ├── alien_signals │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── alien_signals_v0.3 │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── mobx │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── preact_signals │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── reactter │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── riverpod │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── signals_core │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── solidart │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── solidart_v1 │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml └── state_beacon │ ├── main.dart │ ├── pubspec.lock │ └── pubspec.yaml ├── gen_bench_report.dart ├── lib ├── cellx_bench.dart ├── config.dart ├── dynamic_bench.dart ├── framework_type.dart ├── kairo │ ├── avoidable.dart │ ├── broad.dart │ ├── deep.dart │ ├── diamond.dart │ ├── mux.dart │ ├── repeated.dart │ ├── triangle.dart │ ├── unstable.dart │ └── utils.dart ├── kairo_bench.dart ├── mol_bench.dart ├── reactive_framework.dart ├── run_framework_bench.dart ├── s_bench.dart └── utils │ ├── bench_repeat.dart │ ├── create_computed.dart │ ├── create_signal.dart │ ├── dep_graph.dart │ ├── perf.dart │ ├── perf_logging.dart │ └── perf_tests.dart ├── pubspec.lock ├── pubspec.yaml └── renovate.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: medz 2 | -------------------------------------------------------------------------------- /.github/workflows/report.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark Report 2 | on: 3 | push: 4 | branches: ["main"] 5 | schedule: 6 | - cron: "0 0 * * *" 7 | workflow_dispatch: 8 | jobs: 9 | bench: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: dart-lang/setup-dart@v1 16 | with: 17 | sdk: stable 18 | - run: dart pub get 19 | - name: Run benchmarks 20 | run: bash bench.sh 21 | - name: Generate report 22 | run: dart run gen_bench_report.dart 23 | - name: Commit report 24 | run: | 25 | git config --local user.email "${{ github.actor }}@users.noreply.github.com" 26 | git config --local user.name "${{ github.actor }}" 27 | git add . 28 | git commit -m "chore: Update benchmark report [skip ci]" --all -s 29 | git push 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present Seven Du 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dart Reactivity Benchmark [![Pub Version](https://img.shields.io/pub/v/reactivity_benchmark)](https://pub.dev/packages/reactivity_benchmark) 2 | 3 | Benchmark comparing different standalone Dart reactivity/signals frameworks. 4 | 5 | ## Ranking 6 | 7 | 8 | | Rank | Framework | Success Rate | Tests | Time | 9 | |------|-----------|--------------|-------|------| 10 | | 🥇 | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 100.0% | 35/35 | 3.82s | 11 | | 🥈 | [alien_signals](https://github.com/medz/alien-signals-dart) | 100.0% | 35/35 | 4.44s | 12 | | 🥉 | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 100.0% | 35/35 | 5.36s | 13 | | 4 | [preact_signals](https://pub.dev/packages/preact_signals) | 100.0% | 35/35 | 10.33s | 14 | | 5 | [signals](https://github.com/rodydavis/signals.dart) | 100.0% | 35/35 | 11.18s | 15 | | 6 | [mobx](https://github.com/mobxjs/mobx.dart) | 100.0% | 35/35 | 27.82s | 16 | 17 | 18 | 19 | ### **Failed Tests** 20 | 21 | 22 | | Framework | Success Rate | Tests | Time | 23 | |-----------|--------------|-------|------| 24 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 77.1% | 27/35 | 3.48s | 25 | | [riverpod](https://github.com/rrousselGit/riverpod) | 62.9% | 22/35 | 21.94s | 26 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 82.9% | 29/35 | 39.23s | 27 | 28 | 29 | 30 | ## Benchmark results of each framework 31 | 32 | 33 | | Test Case | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | [alien_signals](https://github.com/medz/alien-signals-dart) | [mobx](https://github.com/mobxjs/mobx.dart) | [preact_signals](https://pub.dev/packages/preact_signals) | [riverpod](https://github.com/rrousselGit/riverpod) | [signals](https://github.com/rodydavis/signals.dart) | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | [solidart(v1)](https://github.com/nank1ro/solidart) | [state_beacon](https://github.com/jinyus/dart_beacon) | 34 | |---|---|---|---|---|---|---|---|---|---| 35 | | avoidablePropagation | 173.65ms | 160.70ms | 2.32s | 200.03ms | 1.34s | 209.52ms | 278.04ms | 2.13s | 161.97ms (fail) | 36 | | broadPropagation | 372.00ms | 315.36ms | 4.39s | 447.41ms | 79.83ms (fail) | 458.26ms | 515.02ms | 5.38s | 5.88ms (fail) | 37 | | deepPropagation | 123.49ms | 97.91ms | 1.54s | 176.59ms | 1.90s (fail) | 172.73ms | 168.25ms | 1.99s | 141.85ms (fail) | 38 | | diamond | 235.28ms | 211.23ms | 2.43s | 282.06ms | 2.54s (fail) | 279.27ms | 351.64ms | 3.42s | 183.71ms (fail) | 39 | | mux | 383.66ms | 352.05ms | 1.86s | 406.01ms | 550.13ms (fail) | 410.18ms | 448.93ms | 1.97s | 192.71ms (fail) | 40 | | repeatedObservers | 47.24ms | 50.32ms | 235.86ms | 40.13ms | 374.63ms (fail) | 44.78ms | 81.85ms | 229.07ms | 53.38ms (fail) | 41 | | triangle | 87.41ms | 79.27ms | 752.35ms | 99.21ms | 919.73ms (fail) | 102.36ms | 118.66ms | 1.10s | 76.37ms (fail) | 42 | | unstable | 60.48ms | 69.66ms | 354.96ms | 70.61ms | 616.72ms (fail) | 79.25ms | 97.83ms | 342.08ms | 336.40ms (fail) | 43 | | molBench | 492.96ms | 485.76ms | 570.86ms | 489.53ms | 11.51ms | 486.25ms | 494.95ms | 1.71s | 951μs | 44 | | create_signals | 7.91ms | 25.01ms | 70.87ms | 5.36ms | 27.91ms | 26.91ms | 56.40ms | 72.74ms | 69.37ms | 45 | | comp_0to1 | 21.40ms | 13.09ms | 34.29ms | 17.57ms | 13.62ms | 11.96ms | 28.19ms | 22.39ms | 57.29ms | 46 | | comp_1to1 | 19.37ms | 18.98ms | 43.01ms | 13.49ms | 21.50ms | 28.68ms | 35.94ms | 48.88ms | 61.48ms | 47 | | comp_2to1 | 20.49ms | 15.86ms | 24.94ms | 10.72ms | 35.39ms | 11.88ms | 38.77ms | 26.47ms | 36.78ms | 48 | | comp_4to1 | 1.68ms | 3.30ms | 25.02ms | 8.58ms | 10.01ms | 1.80ms | 15.80ms | 23.53ms | 16.78ms | 49 | | comp_1000to1 | 4μs | 4μs | 17μs | 4μs | 4μs | 5μs | 18μs | 2.81ms | 41μs | 50 | | comp_1to2 | 9.97ms | 15.17ms | 37.00ms | 15.06ms | 10.94ms | 15.99ms | 34.09ms | 20.87ms | 52.55ms | 51 | | comp_1to4 | 4.57ms | 14.37ms | 18.21ms | 28.23ms | 21.22ms | 9.46ms | 17.51ms | 28.14ms | 47.52ms | 52 | | comp_1to8 | 6.87ms | 5.89ms | 21.87ms | 8.03ms | 5.38ms | 9.08ms | 22.96ms | 21.14ms | 43.39ms | 53 | | comp_1to1000 | 3.67ms | 10.41ms | 15.82ms | 5.08ms | 4.57ms | 7.13ms | 16.78ms | 17.39ms | 38.63ms | 54 | | update_1to1 | 4.53ms | 5.60ms | 23.78ms | 8.69ms | 84.64ms | 9.11ms | 15.93ms | 43.41ms | 11.52ms | 55 | | update_2to1 | 2.35ms | 2.85ms | 11.66ms | 4.25ms | 42.10ms | 4.63ms | 7.88ms | 21.61ms | 6.11ms | 56 | | update_4to1 | 1.20ms | 1.56ms | 6.27ms | 2.16ms | 21.01ms | 2.53ms | 4.04ms | 10.96ms | 3.28ms | 57 | | update_1000to1 | 17μs | 26μs | 70μs | 21μs | 178μs | 22μs | 41μs | 117μs | 14μs | 58 | | update_1to2 | 2.37ms | 2.80ms | 10.85ms | 4.59ms | 42.27ms | 4.49ms | 8.01ms | 21.21ms | 6.46ms | 59 | | update_1to4 | 1.24ms | 1.43ms | 7.16ms | 2.15ms | 20.60ms | 2.28ms | 4.01ms | 10.86ms | 3.23ms | 60 | | update_1to1000 | 48μs | 51μs | 174μs | 167μs | 96μs | 43μs | 169μs | 222μs | 405μs | 61 | | cellx1000 | 7.52ms | 31.18ms | 85.87ms | 9.88ms | N/A | 9.96ms | 15.16ms | 164.42ms | 5.42ms | 62 | | cellx2500 | 27.29ms | 136.57ms | 282.46ms | 31.70ms | N/A | 36.01ms | 49.41ms | 514.97ms | 29.73ms | 63 | | cellx5000 | 64.26ms | 428.15ms | 619.16ms | 103.66ms | N/A | 78.39ms | 113.83ms | 1.21s | 83.76ms | 64 | | 10x5 - 2 sources - read 20.0% (simple) | 229.79ms | 236.28ms | 2.01s | 438.78ms | 2.13s | 526.30ms | 366.64ms | 2.59s (partial) | 237.29ms | 65 | | 10x10 - 6 sources - dynamic - read 20.0% (dynamic) | 177.06ms | 183.76ms | 1.58s | 269.21ms | 1.43s (partial) | 279.75ms | 246.75ms | 2.30s (partial) | 198.29ms | 66 | | 1000x12 - 4 sources - dynamic (large) | 308.08ms | 365.85ms | 1.98s | 3.71s | 2.48s (partial) | 3.74s | 472.10ms | 4.02s (partial) | 346.97ms | 67 | | 1000x5 - 25 sources (wide dense) | 458.75ms | 624.10ms | 3.55s | 2.74s | 4.07s | 3.42s | 593.85ms | 5.06s (partial) | 500.93ms | 68 | | 5x500 - 3 sources (deep) | 201.67ms | 193.45ms | 1.18s | 227.13ms | 1.39s | 223.47ms | 253.22ms | 1.98s (partial) | 206.57ms | 69 | | 100x15 - 6 sources - dynamic (very dynamic) | 259.60ms | 281.18ms | 1.73s | 460.71ms | 1.75s (partial) | 479.49ms | 385.02ms | 2.73s (partial) | 259.88ms | 70 | 71 | 72 | 73 | > [!TIP] 74 | > - `(fail)`: Test case failed 75 | > - `(partial)`: Partial of the test cases failed 76 | 77 | ## Integrate into your project 78 | 79 | You can easily integrate Dart reactivity benchmark into your project to provide benchmarking. 80 | 81 | ### Install it 82 | 83 | ```bash 84 | dart pub add dev:reactivity_benchmark 85 | ``` 86 | 87 | ### Writing Tests 88 | 89 | ```dart 90 | class YourReactiveFramework extends ReactiveFramework { 91 | ... 92 | } 93 | 94 | void main() { 95 | final framework = YourReactiveFramework(); 96 | runFrameworkBench(framework); 97 | } 98 | ``` 99 | 100 | ## Local run benchmarks 101 | 102 | Dart VM 103 | ```bash 104 | dart run frameworks/[framework_name].dart 105 | ``` 106 | 107 | Run all benchamrks 108 | ```bash 109 | bash bench.sh 110 | ``` 111 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Initialize frameworks array 4 | frameworks=() 5 | 6 | # Get list of framework directories 7 | for dir in frameworks/*/; do 8 | # Extract just the directory name 9 | framework=$(basename "$dir") 10 | frameworks+=("$framework") 11 | done 12 | 13 | if [ ${#frameworks[@]} -eq 0 ]; then 14 | echo "Error: No framework directories found in frameworks directory." 15 | exit 1 16 | fi 17 | 18 | skipFramework=("reactter"); 19 | 20 | for framework in "${frameworks[@]}" 21 | do 22 | if [[ " ${skipFramework[*]} " =~ " $framework " ]]; then 23 | echo "Skipping $framework as it's in the skip list" 24 | continue 25 | fi 26 | 27 | echo "==================== $framework ====================" 28 | echo "$(date)" 29 | 30 | # Current directory 31 | cwd=$(pwd) 32 | 33 | # Enter framework directory 34 | cd "frameworks/$framework" 35 | 36 | # Install deps 37 | dart pub get 38 | 39 | # compile to native 40 | echo "Compiling $framework to native..." 41 | dart compile exe "main.dart" -o "$framework" || exit 1 42 | 43 | echo "Running benchmark for $framework..." 44 | { ./$framework | tee "$cwd/bench/$framework.md"; } 2>&1; 45 | rm "$framework" 46 | if [ $? -ne 0 ]; then 47 | echo "Error running benchmark for $framework" 48 | exit 1 49 | fi 50 | 51 | # Back to root directory 52 | cd "$cwd" 53 | done 54 | -------------------------------------------------------------------------------- /bench/alien_signals.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [alien_signals](https://github.com/medz/alien-signals-dart) | avoidablePropagation (success) | 160699 | 4 | | [alien_signals](https://github.com/medz/alien-signals-dart) | broadPropagation (success) | 315355 | 5 | | [alien_signals](https://github.com/medz/alien-signals-dart) | deepPropagation (success) | 97905 | 6 | | [alien_signals](https://github.com/medz/alien-signals-dart) | diamond (success) | 211229 | 7 | | [alien_signals](https://github.com/medz/alien-signals-dart) | mux (success) | 352046 | 8 | | [alien_signals](https://github.com/medz/alien-signals-dart) | repeatedObservers (success) | 50319 | 9 | | [alien_signals](https://github.com/medz/alien-signals-dart) | triangle (success) | 79272 | 10 | | [alien_signals](https://github.com/medz/alien-signals-dart) | unstable (success) | 69656 | 11 | | [alien_signals](https://github.com/medz/alien-signals-dart) | molBench | 485760 | 12 | | [alien_signals](https://github.com/medz/alien-signals-dart) | create_signals | 25013 | 13 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_0to1 | 13090 | 14 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1to1 | 18985 | 15 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_2to1 | 15861 | 16 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_4to1 | 3296 | 17 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1000to1 | 4 | 18 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1to2 | 15173 | 19 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1to4 | 14369 | 20 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1to8 | 5895 | 21 | | [alien_signals](https://github.com/medz/alien-signals-dart) | comp_1to1000 | 10406 | 22 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_1to1 | 5598 | 23 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_2to1 | 2846 | 24 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_4to1 | 1558 | 25 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_1000to1 | 26 | 26 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_1to2 | 2799 | 27 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_1to4 | 1433 | 28 | | [alien_signals](https://github.com/medz/alien-signals-dart) | update_1to1000 | 51 | 29 | | [alien_signals](https://github.com/medz/alien-signals-dart) | cellx1000 (first: pass, last: pass) | 31185 | 30 | | [alien_signals](https://github.com/medz/alien-signals-dart) | cellx2500 (first: pass, last: pass) | 136570 | 31 | | [alien_signals](https://github.com/medz/alien-signals-dart) | cellx5000 (first: pass, last: pass) | 428148 | 32 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 236283 | 33 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 183763 | 34 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 365852 | 35 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 624099 | 36 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 193454 | 37 | | [alien_signals](https://github.com/medz/alien-signals-dart) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 281180 | 38 | -------------------------------------------------------------------------------- /bench/alien_signals_v0.3.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | avoidablePropagation (success) | 173647 | 4 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | broadPropagation (success) | 372002 | 5 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | deepPropagation (success) | 123494 | 6 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | diamond (success) | 235280 | 7 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | mux (success) | 383660 | 8 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | repeatedObservers (success) | 47245 | 9 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | triangle (success) | 87413 | 10 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | unstable (success) | 60477 | 11 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | molBench | 492962 | 12 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | create_signals | 7906 | 13 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_0to1 | 21402 | 14 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1to1 | 19371 | 15 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_2to1 | 20490 | 16 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_4to1 | 1683 | 17 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1000to1 | 4 | 18 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1to2 | 9972 | 19 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1to4 | 4568 | 20 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1to8 | 6867 | 21 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | comp_1to1000 | 3667 | 22 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_1to1 | 4534 | 23 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_2to1 | 2354 | 24 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_4to1 | 1197 | 25 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_1000to1 | 17 | 26 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_1to2 | 2372 | 27 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_1to4 | 1237 | 28 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | update_1to1000 | 48 | 29 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | cellx1000 (first: pass, last: pass) | 7523 | 30 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | cellx2500 (first: pass, last: pass) | 27287 | 31 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | cellx5000 (first: pass, last: pass) | 64261 | 32 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 229789 | 33 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 177061 | 34 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 308083 | 35 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 458755 | 36 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 201667 | 37 | | [alien_signals(v0.3)](https://github.com/medz/alien-signals-dart) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 259595 | 38 | -------------------------------------------------------------------------------- /bench/mobx.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [mobx](https://github.com/mobxjs/mobx.dart) | avoidablePropagation (success) | 2324688 | 4 | | [mobx](https://github.com/mobxjs/mobx.dart) | broadPropagation (success) | 4392421 | 5 | | [mobx](https://github.com/mobxjs/mobx.dart) | deepPropagation (success) | 1536974 | 6 | | [mobx](https://github.com/mobxjs/mobx.dart) | diamond (success) | 2429509 | 7 | | [mobx](https://github.com/mobxjs/mobx.dart) | mux (success) | 1858162 | 8 | | [mobx](https://github.com/mobxjs/mobx.dart) | repeatedObservers (success) | 235860 | 9 | | [mobx](https://github.com/mobxjs/mobx.dart) | triangle (success) | 752350 | 10 | | [mobx](https://github.com/mobxjs/mobx.dart) | unstable (success) | 354961 | 11 | | [mobx](https://github.com/mobxjs/mobx.dart) | molBench | 570860 | 12 | | [mobx](https://github.com/mobxjs/mobx.dart) | create_signals | 70866 | 13 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_0to1 | 34288 | 14 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1to1 | 43006 | 15 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_2to1 | 24938 | 16 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_4to1 | 25017 | 17 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1000to1 | 17 | 18 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1to2 | 37001 | 19 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1to4 | 18208 | 20 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1to8 | 21866 | 21 | | [mobx](https://github.com/mobxjs/mobx.dart) | comp_1to1000 | 15824 | 22 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_1to1 | 23777 | 23 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_2to1 | 11663 | 24 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_4to1 | 6271 | 25 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_1000to1 | 70 | 26 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_1to2 | 10849 | 27 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_1to4 | 7163 | 28 | | [mobx](https://github.com/mobxjs/mobx.dart) | update_1to1000 | 174 | 29 | | [mobx](https://github.com/mobxjs/mobx.dart) | cellx1000 (first: pass, last: pass) | 85873 | 30 | | [mobx](https://github.com/mobxjs/mobx.dart) | cellx2500 (first: pass, last: pass) | 282459 | 31 | | [mobx](https://github.com/mobxjs/mobx.dart) | cellx5000 (first: pass, last: pass) | 619156 | 32 | | [mobx](https://github.com/mobxjs/mobx.dart) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 2012014 | 33 | | [mobx](https://github.com/mobxjs/mobx.dart) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 1575480 | 34 | | [mobx](https://github.com/mobxjs/mobx.dart) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 1984560 | 35 | | [mobx](https://github.com/mobxjs/mobx.dart) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 3549769 | 36 | | [mobx](https://github.com/mobxjs/mobx.dart) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 1178355 | 37 | | [mobx](https://github.com/mobxjs/mobx.dart) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 1725224 | 38 | -------------------------------------------------------------------------------- /bench/preact_signals.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [preact_signals](https://pub.dev/packages/preact_signals) | avoidablePropagation (success) | 200033 | 4 | | [preact_signals](https://pub.dev/packages/preact_signals) | broadPropagation (success) | 447412 | 5 | | [preact_signals](https://pub.dev/packages/preact_signals) | deepPropagation (success) | 176589 | 6 | | [preact_signals](https://pub.dev/packages/preact_signals) | diamond (success) | 282060 | 7 | | [preact_signals](https://pub.dev/packages/preact_signals) | mux (success) | 406014 | 8 | | [preact_signals](https://pub.dev/packages/preact_signals) | repeatedObservers (success) | 40125 | 9 | | [preact_signals](https://pub.dev/packages/preact_signals) | triangle (success) | 99209 | 10 | | [preact_signals](https://pub.dev/packages/preact_signals) | unstable (success) | 70606 | 11 | | [preact_signals](https://pub.dev/packages/preact_signals) | molBench | 489527 | 12 | | [preact_signals](https://pub.dev/packages/preact_signals) | create_signals | 5358 | 13 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_0to1 | 17567 | 14 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1to1 | 13489 | 15 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_2to1 | 10717 | 16 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_4to1 | 8578 | 17 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1000to1 | 4 | 18 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1to2 | 15057 | 19 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1to4 | 28234 | 20 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1to8 | 8032 | 21 | | [preact_signals](https://pub.dev/packages/preact_signals) | comp_1to1000 | 5082 | 22 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_1to1 | 8685 | 23 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_2to1 | 4245 | 24 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_4to1 | 2158 | 25 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_1000to1 | 21 | 26 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_1to2 | 4591 | 27 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_1to4 | 2147 | 28 | | [preact_signals](https://pub.dev/packages/preact_signals) | update_1to1000 | 167 | 29 | | [preact_signals](https://pub.dev/packages/preact_signals) | cellx1000 (first: pass, last: pass) | 9881 | 30 | | [preact_signals](https://pub.dev/packages/preact_signals) | cellx2500 (first: pass, last: pass) | 31704 | 31 | | [preact_signals](https://pub.dev/packages/preact_signals) | cellx5000 (first: pass, last: pass) | 103657 | 32 | | [preact_signals](https://pub.dev/packages/preact_signals) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 438779 | 33 | | [preact_signals](https://pub.dev/packages/preact_signals) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 269212 | 34 | | [preact_signals](https://pub.dev/packages/preact_signals) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 3709722 | 35 | | [preact_signals](https://pub.dev/packages/preact_signals) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 2735032 | 36 | | [preact_signals](https://pub.dev/packages/preact_signals) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 227126 | 37 | | [preact_signals](https://pub.dev/packages/preact_signals) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 460714 | 38 | -------------------------------------------------------------------------------- /bench/riverpod.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [riverpod](https://github.com/rrousselGit/riverpod) | avoidablePropagation (success) | 1342531 | 4 | | [riverpod](https://github.com/rrousselGit/riverpod) | broadPropagation (fail) | 79832 | 5 | | [riverpod](https://github.com/rrousselGit/riverpod) | deepPropagation (fail) | 1895973 | 6 | | [riverpod](https://github.com/rrousselGit/riverpod) | diamond (fail) | 2539802 | 7 | | [riverpod](https://github.com/rrousselGit/riverpod) | mux (fail) | 550135 | 8 | | [riverpod](https://github.com/rrousselGit/riverpod) | repeatedObservers (fail) | 374635 | 9 | | [riverpod](https://github.com/rrousselGit/riverpod) | triangle (fail) | 919734 | 10 | | [riverpod](https://github.com/rrousselGit/riverpod) | unstable (fail) | 616717 | 11 | | [riverpod](https://github.com/rrousselGit/riverpod) | molBench | 11508 | 12 | | [riverpod](https://github.com/rrousselGit/riverpod) | create_signals | 27909 | 13 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_0to1 | 13615 | 14 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1to1 | 21503 | 15 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_2to1 | 35389 | 16 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_4to1 | 10012 | 17 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1000to1 | 4 | 18 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1to2 | 10938 | 19 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1to4 | 21217 | 20 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1to8 | 5382 | 21 | | [riverpod](https://github.com/rrousselGit/riverpod) | comp_1to1000 | 4574 | 22 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_1to1 | 84637 | 23 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_2to1 | 42103 | 24 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_4to1 | 21007 | 25 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_1000to1 | 178 | 26 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_1to2 | 42265 | 27 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_1to4 | 20598 | 28 | | [riverpod](https://github.com/rrousselGit/riverpod) | update_1to1000 | 96 | 29 | | [riverpod](https://github.com/rrousselGit/riverpod) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 2129218 | 30 | | [riverpod](https://github.com/rrousselGit/riverpod) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: fail) | 1433683 | 31 | | [riverpod](https://github.com/rrousselGit/riverpod) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: fail) | 2478282 | 32 | | [riverpod](https://github.com/rrousselGit/riverpod) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 4070831 | 33 | | [riverpod](https://github.com/rrousselGit/riverpod) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 1387465 | 34 | | [riverpod](https://github.com/rrousselGit/riverpod) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: fail) | 1750640 | 35 | -------------------------------------------------------------------------------- /bench/signals_core.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [signals](https://github.com/rodydavis/signals.dart) | avoidablePropagation (success) | 209524 | 4 | | [signals](https://github.com/rodydavis/signals.dart) | broadPropagation (success) | 458256 | 5 | | [signals](https://github.com/rodydavis/signals.dart) | deepPropagation (success) | 172729 | 6 | | [signals](https://github.com/rodydavis/signals.dart) | diamond (success) | 279274 | 7 | | [signals](https://github.com/rodydavis/signals.dart) | mux (success) | 410183 | 8 | | [signals](https://github.com/rodydavis/signals.dart) | repeatedObservers (success) | 44777 | 9 | | [signals](https://github.com/rodydavis/signals.dart) | triangle (success) | 102355 | 10 | | [signals](https://github.com/rodydavis/signals.dart) | unstable (success) | 79248 | 11 | | [signals](https://github.com/rodydavis/signals.dart) | molBench | 486247 | 12 | | [signals](https://github.com/rodydavis/signals.dart) | create_signals | 26905 | 13 | | [signals](https://github.com/rodydavis/signals.dart) | comp_0to1 | 11956 | 14 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1to1 | 28675 | 15 | | [signals](https://github.com/rodydavis/signals.dart) | comp_2to1 | 11875 | 16 | | [signals](https://github.com/rodydavis/signals.dart) | comp_4to1 | 1801 | 17 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1000to1 | 5 | 18 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1to2 | 15990 | 19 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1to4 | 9462 | 20 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1to8 | 9079 | 21 | | [signals](https://github.com/rodydavis/signals.dart) | comp_1to1000 | 7131 | 22 | | [signals](https://github.com/rodydavis/signals.dart) | update_1to1 | 9110 | 23 | | [signals](https://github.com/rodydavis/signals.dart) | update_2to1 | 4634 | 24 | | [signals](https://github.com/rodydavis/signals.dart) | update_4to1 | 2529 | 25 | | [signals](https://github.com/rodydavis/signals.dart) | update_1000to1 | 22 | 26 | | [signals](https://github.com/rodydavis/signals.dart) | update_1to2 | 4493 | 27 | | [signals](https://github.com/rodydavis/signals.dart) | update_1to4 | 2277 | 28 | | [signals](https://github.com/rodydavis/signals.dart) | update_1to1000 | 43 | 29 | | [signals](https://github.com/rodydavis/signals.dart) | cellx1000 (first: pass, last: pass) | 9962 | 30 | | [signals](https://github.com/rodydavis/signals.dart) | cellx2500 (first: pass, last: pass) | 36011 | 31 | | [signals](https://github.com/rodydavis/signals.dart) | cellx5000 (first: pass, last: pass) | 78385 | 32 | | [signals](https://github.com/rodydavis/signals.dart) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 526301 | 33 | | [signals](https://github.com/rodydavis/signals.dart) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 279752 | 34 | | [signals](https://github.com/rodydavis/signals.dart) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 3739506 | 35 | | [signals](https://github.com/rodydavis/signals.dart) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 3423300 | 36 | | [signals](https://github.com/rodydavis/signals.dart) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 223473 | 37 | | [signals](https://github.com/rodydavis/signals.dart) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 479485 | 38 | -------------------------------------------------------------------------------- /bench/solidart.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | avoidablePropagation (success) | 278043 | 4 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | broadPropagation (success) | 515017 | 5 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | deepPropagation (success) | 168246 | 6 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | diamond (success) | 351641 | 7 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | mux (success) | 448929 | 8 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | repeatedObservers (success) | 81854 | 9 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | triangle (success) | 118657 | 10 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | unstable (success) | 97829 | 11 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | molBench | 494954 | 12 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | create_signals | 56395 | 13 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_0to1 | 28187 | 14 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1to1 | 35936 | 15 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_2to1 | 38770 | 16 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_4to1 | 15803 | 17 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1000to1 | 18 | 18 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1to2 | 34094 | 19 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1to4 | 17510 | 20 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1to8 | 22960 | 21 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | comp_1to1000 | 16777 | 22 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_1to1 | 15931 | 23 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_2to1 | 7879 | 24 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_4to1 | 4040 | 25 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_1000to1 | 41 | 26 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_1to2 | 8008 | 27 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_1to4 | 4011 | 28 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | update_1to1000 | 169 | 29 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | cellx1000 (first: pass, last: pass) | 15161 | 30 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | cellx2500 (first: pass, last: pass) | 49412 | 31 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | cellx5000 (first: pass, last: pass) | 113827 | 32 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 366642 | 33 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 246754 | 34 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 472096 | 35 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 593848 | 36 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 253225 | 37 | | [solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 385020 | 38 | -------------------------------------------------------------------------------- /bench/solidart_v1.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [solidart(v1)](https://github.com/nank1ro/solidart) | avoidablePropagation (success) | 2130213 | 4 | | [solidart(v1)](https://github.com/nank1ro/solidart) | broadPropagation (success) | 5384015 | 5 | | [solidart(v1)](https://github.com/nank1ro/solidart) | deepPropagation (success) | 1989922 | 6 | | [solidart(v1)](https://github.com/nank1ro/solidart) | diamond (success) | 3417766 | 7 | | [solidart(v1)](https://github.com/nank1ro/solidart) | mux (success) | 1974648 | 8 | | [solidart(v1)](https://github.com/nank1ro/solidart) | repeatedObservers (success) | 229070 | 9 | | [solidart(v1)](https://github.com/nank1ro/solidart) | triangle (success) | 1102643 | 10 | | [solidart(v1)](https://github.com/nank1ro/solidart) | unstable (success) | 342079 | 11 | | [solidart(v1)](https://github.com/nank1ro/solidart) | molBench | 1707466 | 12 | | [solidart(v1)](https://github.com/nank1ro/solidart) | create_signals | 72736 | 13 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_0to1 | 22388 | 14 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1to1 | 48877 | 15 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_2to1 | 26466 | 16 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_4to1 | 23534 | 17 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1000to1 | 2812 | 18 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1to2 | 20870 | 19 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1to4 | 28138 | 20 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1to8 | 21137 | 21 | | [solidart(v1)](https://github.com/nank1ro/solidart) | comp_1to1000 | 17393 | 22 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_1to1 | 43411 | 23 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_2to1 | 21606 | 24 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_4to1 | 10965 | 25 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_1000to1 | 117 | 26 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_1to2 | 21212 | 27 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_1to4 | 10864 | 28 | | [solidart(v1)](https://github.com/nank1ro/solidart) | update_1to1000 | 222 | 29 | | [solidart(v1)](https://github.com/nank1ro/solidart) | cellx1000 (first: pass, last: pass) | 164417 | 30 | | [solidart(v1)](https://github.com/nank1ro/solidart) | cellx2500 (first: pass, last: pass) | 514970 | 31 | | [solidart(v1)](https://github.com/nank1ro/solidart) | cellx5000 (first: pass, last: pass) | 1207294 | 32 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: fail) | 2588054 | 33 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: fail) | 2299026 | 34 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: fail) | 4015346 | 35 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 1000x5 - 25 sources (wide dense, sum: pass, count: fail) | 5057638 | 36 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 5x500 - 3 sources (deep, sum: pass, count: fail) | 1979305 | 37 | | [solidart(v1)](https://github.com/nank1ro/solidart) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: fail) | 2730881 | 38 | -------------------------------------------------------------------------------- /bench/state_beacon.md: -------------------------------------------------------------------------------- 1 | | Framework | Test Case | Time (μs) | 2 | | --- | --- | --- | 3 | | [state_beacon](https://github.com/jinyus/dart_beacon) | avoidablePropagation (fail) | 161971 | 4 | | [state_beacon](https://github.com/jinyus/dart_beacon) | broadPropagation (fail) | 5879 | 5 | | [state_beacon](https://github.com/jinyus/dart_beacon) | deepPropagation (fail) | 141849 | 6 | | [state_beacon](https://github.com/jinyus/dart_beacon) | diamond (fail) | 183713 | 7 | | [state_beacon](https://github.com/jinyus/dart_beacon) | mux (fail) | 192712 | 8 | | [state_beacon](https://github.com/jinyus/dart_beacon) | repeatedObservers (fail) | 53380 | 9 | | [state_beacon](https://github.com/jinyus/dart_beacon) | triangle (fail) | 76372 | 10 | | [state_beacon](https://github.com/jinyus/dart_beacon) | unstable (fail) | 336405 | 11 | | [state_beacon](https://github.com/jinyus/dart_beacon) | molBench | 951 | 12 | | [state_beacon](https://github.com/jinyus/dart_beacon) | create_signals | 69369 | 13 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_0to1 | 57288 | 14 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1to1 | 61479 | 15 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_2to1 | 36778 | 16 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_4to1 | 16780 | 17 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1000to1 | 41 | 18 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1to2 | 52545 | 19 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1to4 | 47520 | 20 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1to8 | 43386 | 21 | | [state_beacon](https://github.com/jinyus/dart_beacon) | comp_1to1000 | 38626 | 22 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_1to1 | 11516 | 23 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_2to1 | 6108 | 24 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_4to1 | 3280 | 25 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_1000to1 | 14 | 26 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_1to2 | 6462 | 27 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_1to4 | 3227 | 28 | | [state_beacon](https://github.com/jinyus/dart_beacon) | update_1to1000 | 405 | 29 | | [state_beacon](https://github.com/jinyus/dart_beacon) | cellx1000 (first: pass, last: pass) | 5425 | 30 | | [state_beacon](https://github.com/jinyus/dart_beacon) | cellx2500 (first: pass, last: pass) | 29726 | 31 | | [state_beacon](https://github.com/jinyus/dart_beacon) | cellx5000 (first: pass, last: pass) | 83759 | 32 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 10x5 - 2 sources - read 20.0% (simple, sum: pass, count: pass) | 237291 | 33 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 10x10 - 6 sources - dynamic - read 20.0% (dynamic, sum: pass, count: pass) | 198288 | 34 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 1000x12 - 4 sources - dynamic (large, sum: pass, count: pass) | 346969 | 35 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 1000x5 - 25 sources (wide dense, sum: pass, count: pass) | 500932 | 36 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 5x500 - 3 sources (deep, sum: pass, count: pass) | 206571 | 37 | | [state_beacon](https://github.com/jinyus/dart_beacon) | 100x15 - 6 sources - dynamic (very dynamic, sum: pass, count: pass) | 259876 | 38 | -------------------------------------------------------------------------------- /frameworks/alien_signals/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:alien_signals/alien_signals.dart' as alien; 2 | import 'package:reactivity_benchmark/reactive_framework.dart'; 3 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class AlientSignalsReactiveFramework extends ReactiveFramework { 8 | const AlientSignalsReactiveFramework() 9 | : super('[alien_signals](https://github.com/medz/alien-signals-dart)'); 10 | 11 | @override 12 | Computed computed(T Function() fn) { 13 | final computed = alien.computed((_) => fn()); 14 | return createComputed(computed); 15 | } 16 | 17 | @override 18 | void effect(void Function() fn) { 19 | alien.effect(fn); 20 | } 21 | 22 | @override 23 | Signal signal(T value) { 24 | final signal = alien.signal(value); 25 | return createSignal(signal, signal); 26 | } 27 | 28 | @override 29 | void withBatch(T Function() fn) { 30 | alien.startBatch(); 31 | fn(); 32 | alien.endBatch(); 33 | } 34 | 35 | @override 36 | T withBuild(T Function() fn) => fn(); 37 | } 38 | 39 | void main() { 40 | runFrameworkBench( 41 | const AlientSignalsReactiveFramework(), 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /frameworks/alien_signals/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | alien_signals: 5 | dependency: "direct main" 6 | description: 7 | name: alien_signals 8 | sha256: "08491dce8f811089ed0bbafb9e09b239dd32afd209d6e496ff71fb0cf4855271" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "0.4.0" 12 | reactivity_benchmark: 13 | dependency: "direct main" 14 | description: 15 | path: "../.." 16 | relative: true 17 | source: path 18 | version: "0.0.1" 19 | sdks: 20 | dart: ">=3.6.0 <4.0.0" 21 | -------------------------------------------------------------------------------- /frameworks/alien_signals/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | alien_signals: ^0.4.0 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/alien_signals_v0.3/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:alien_signals/preset.dart' as alien; 2 | import 'package:reactivity_benchmark/reactive_framework.dart'; 3 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class AlientSignalsReactiveFramework extends ReactiveFramework { 8 | const AlientSignalsReactiveFramework() 9 | : super( 10 | '[alien_signals(v0.3)](https://github.com/medz/alien-signals-dart)'); 11 | 12 | @override 13 | Computed computed(T Function() fn) { 14 | final computed = alien.computed((_) => fn()); 15 | return createComputed(computed.call); 16 | } 17 | 18 | @override 19 | void effect(void Function() fn) { 20 | alien.effect(fn); 21 | } 22 | 23 | @override 24 | Signal signal(T value) { 25 | final inner = alien.signal(value); 26 | return createSignal(inner.call, inner.call); 27 | } 28 | 29 | @override 30 | void withBatch(T Function() fn) { 31 | alien.startBatch(); 32 | fn(); 33 | alien.endBatch(); 34 | } 35 | 36 | @override 37 | T withBuild(T Function() fn) => fn(); 38 | } 39 | 40 | void main() { 41 | runFrameworkBench( 42 | const AlientSignalsReactiveFramework(), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /frameworks/alien_signals_v0.3/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | alien_signals: 5 | dependency: "direct main" 6 | description: 7 | name: alien_signals 8 | sha256: "00826ee362d176abbfa193bfde3dc23c11439564544568324d1a4a96c5705978" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "0.3.0" 12 | reactivity_benchmark: 13 | dependency: "direct main" 14 | description: 15 | path: "../.." 16 | relative: true 17 | source: path 18 | version: "0.0.1" 19 | sdks: 20 | dart: ">=3.6.0 <4.0.0" 21 | -------------------------------------------------------------------------------- /frameworks/alien_signals_v0.3/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | alien_signals: 0.3.0 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/mobx/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:mobx/mobx.dart' as mobx; 2 | import 'package:reactivity_benchmark/reactive_framework.dart'; 3 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class MobxFramework extends ReactiveFramework { 8 | const MobxFramework() : super('[mobx](https://github.com/mobxjs/mobx.dart)'); 9 | 10 | @override 11 | Computed computed(T Function() fn) { 12 | final computed = mobx.Computed(() => fn()); 13 | return createComputed(() => computed.value); 14 | } 15 | 16 | @override 17 | void effect(void Function() fn) { 18 | mobx.autorun((_) => fn()); 19 | } 20 | 21 | @override 22 | Signal signal(T value) { 23 | final observable = mobx.Observable(value); 24 | return createSignal( 25 | () => observable.value, 26 | (value) => observable.value = value, 27 | ); 28 | } 29 | 30 | @override 31 | void withBatch(T Function() fn) { 32 | mobx.runInAction(fn); 33 | } 34 | 35 | @override 36 | T withBuild(T Function() fn) { 37 | return fn(); 38 | } 39 | } 40 | 41 | void main() { 42 | const framework = MobxFramework(); 43 | runFrameworkBench(framework); 44 | } 45 | -------------------------------------------------------------------------------- /frameworks/mobx/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | collection: 5 | dependency: transitive 6 | description: 7 | name: collection 8 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.19.1" 12 | meta: 13 | dependency: transitive 14 | description: 15 | name: meta 16 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.16.0" 20 | mobx: 21 | dependency: "direct main" 22 | description: 23 | name: mobx 24 | sha256: bf1a90e5bcfd2851fc6984e20eef69557c65d9e4d0a88f5be4cf72c9819ce6b0 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.5.0" 28 | reactivity_benchmark: 29 | dependency: "direct main" 30 | description: 31 | path: "../.." 32 | relative: true 33 | source: path 34 | version: "0.0.1" 35 | sdks: 36 | dart: ">=3.6.0 <4.0.0" 37 | -------------------------------------------------------------------------------- /frameworks/mobx/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | mobx: ^2.5.0 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/preact_signals/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 2 | import 'package:preact_signals/preact_signals.dart' as preact_signals; 3 | import 'package:reactivity_benchmark/reactive_framework.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class _PreactSignalsReactiveFramework extends ReactiveFramework { 8 | const _PreactSignalsReactiveFramework() 9 | : super('[preact_signals](https://pub.dev/packages/preact_signals)'); 10 | 11 | @override 12 | Computed computed(T Function() fn) { 13 | final inner = preact_signals.computed(() => fn()); 14 | return createComputed(() => inner.value); 15 | } 16 | 17 | @override 18 | void effect(void Function() fn) { 19 | preact_signals.effect(fn); 20 | } 21 | 22 | @override 23 | Signal signal(T value) { 24 | final inner = preact_signals.signal(value); 25 | return createSignal( 26 | () => inner.value, 27 | (value) => inner.value = value, 28 | ); 29 | } 30 | 31 | @override 32 | void withBatch(T Function() fn) { 33 | preact_signals.batch(fn); 34 | } 35 | 36 | @override 37 | T withBuild(T Function() fn) => fn(); 38 | } 39 | 40 | void main() { 41 | runFrameworkBench( 42 | const _PreactSignalsReactiveFramework(), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /frameworks/preact_signals/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | meta: 5 | dependency: transitive 6 | description: 7 | name: meta 8 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.16.0" 12 | preact_signals: 13 | dependency: "direct main" 14 | description: 15 | name: preact_signals 16 | sha256: a5b445796a02244b85fa43d79a7030fbfbbb08f7c48277ee93c636140a8fb2e0 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.8.3" 20 | reactivity_benchmark: 21 | dependency: "direct main" 22 | description: 23 | path: "../.." 24 | relative: true 25 | source: path 26 | version: "0.0.1" 27 | sdks: 28 | dart: ">=3.6.0 <4.0.0" 29 | -------------------------------------------------------------------------------- /frameworks/preact_signals/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | preact_signals: ^1.8.3 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/reactter/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:reactter/reactter.dart' as reactter; 2 | import 'package:reactivity_benchmark/reactive_framework.dart'; 3 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | class ReactterReactiveFramework extends ReactiveFramework { 8 | ReactterReactiveFramework() 9 | : super("[reactter](https://github.com/2devs-team/reactter)"); 10 | 11 | var states = []; 12 | 13 | @override 14 | Signal signal(T value) { 15 | final signal = reactter.Signal(value); 16 | return createSignal( 17 | () { 18 | states.add(signal); 19 | return signal.value; 20 | }, 21 | (value) => signal.value = value, 22 | ); 23 | } 24 | 25 | @override 26 | Computed computed(T Function() fn) { 27 | bool ready = false; 28 | final deps = []; 29 | final compute = reactter.UseCompute(() { 30 | if (ready) return fn(); 31 | 32 | final prev = states; 33 | states = []; 34 | try { 35 | return fn(); 36 | } finally { 37 | deps.addAll(states); 38 | states = prev; 39 | ready = true; 40 | } 41 | }, deps); 42 | return createComputed(() { 43 | try { 44 | states.add(compute); 45 | return compute.value; 46 | } finally { 47 | deps.clear(); 48 | } 49 | }); 50 | } 51 | 52 | @override 53 | void effect(void Function() fn) { 54 | final deps = []; 55 | reactter.UseEffect.runOnInit(() { 56 | final prev = states; 57 | states = []; 58 | try { 59 | fn(); 60 | } finally { 61 | deps.addAll(states); 62 | states = prev; 63 | } 64 | }, deps); 65 | deps.clear(); 66 | } 67 | 68 | @override 69 | void withBatch(T Function() fn) { 70 | reactter.Rt.batch(fn); 71 | } 72 | 73 | @override 74 | T withBuild(T Function() fn) { 75 | return fn(); 76 | } 77 | } 78 | 79 | void main() { 80 | runFrameworkBench(ReactterReactiveFramework()); 81 | } 82 | -------------------------------------------------------------------------------- /frameworks/reactter/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | meta: 5 | dependency: transitive 6 | description: 7 | name: meta 8 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.16.0" 12 | reactivity_benchmark: 13 | dependency: "direct main" 14 | description: 15 | path: "../.." 16 | relative: true 17 | source: path 18 | version: "0.0.1" 19 | reactter: 20 | dependency: "direct main" 21 | description: 22 | name: reactter 23 | sha256: "647f06d1b42e294dad1b9a9c7820344829781379e1207cedb49b3cf74db3873c" 24 | url: "https://pub.dev" 25 | source: hosted 26 | version: "8.0.0" 27 | sdks: 28 | dart: ">=3.6.0 <4.0.0" 29 | -------------------------------------------------------------------------------- /frameworks/reactter/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | reactter: ^8.0.0 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/riverpod/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod/riverpod.dart' as riverpod; 2 | import 'package:reactivity_benchmark/dynamic_bench.dart'; 3 | import 'package:reactivity_benchmark/kairo_bench.dart'; 4 | import 'package:reactivity_benchmark/mol_bench.dart'; 5 | import 'package:reactivity_benchmark/s_bench.dart'; 6 | import 'package:reactivity_benchmark/utils/perf_logging.dart'; 7 | import 'package:reactivity_benchmark/reactive_framework.dart'; 8 | // import 'package:reactivity_benchmark/run_framework_bench.dart'; 9 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 10 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 11 | 12 | class RiverpodReactiveFramework extends ReactiveFramework { 13 | RiverpodReactiveFramework(this.root) 14 | : super('[riverpod](https://github.com/rrousselGit/riverpod)'); 15 | 16 | final riverpod.ProviderContainer root; 17 | riverpod.Ref? ref; 18 | 19 | @override 20 | Signal signal(T value) { 21 | final state = riverpod.StateProvider((ref) { 22 | final prev = this.ref; 23 | try { 24 | this.ref = ref; 25 | return value; 26 | } finally { 27 | this.ref = prev; 28 | } 29 | }); 30 | 31 | return createSignal( 32 | () { 33 | if (ref != null) return ref!.watch(state); 34 | return root.read(state); 35 | }, 36 | (value) { 37 | root.read(state.notifier).state = value; 38 | }, 39 | ); 40 | } 41 | 42 | @override 43 | Computed computed(T Function() fn) { 44 | final provider = riverpod.Provider((ref) { 45 | final prev = this.ref; 46 | try { 47 | this.ref = ref; 48 | return fn(); 49 | } finally { 50 | this.ref = prev; 51 | } 52 | }); 53 | final computed = createComputed(() { 54 | if (ref != null) return ref!.watch(provider); 55 | return root.read(provider); 56 | }); 57 | 58 | return computed; 59 | } 60 | 61 | @override 62 | void effect(void Function() fn) { 63 | final provider = riverpod.Provider((ref) { 64 | final prev = this.ref; 65 | try { 66 | this.ref = ref; 67 | fn(); 68 | } finally { 69 | this.ref = prev; 70 | } 71 | }); 72 | root.listen(provider, (_, __) => fn()); 73 | } 74 | 75 | @override 76 | void withBatch(T Function() fn) { 77 | fn(); 78 | } 79 | 80 | @override 81 | T withBuild(T Function() fn) { 82 | return fn(); 83 | } 84 | } 85 | 86 | main() async { 87 | final container = riverpod.ProviderContainer(); 88 | final framework = RiverpodReactiveFramework(container); 89 | printPerfReportHeaders(); 90 | await kairoBench(framework); 91 | await molBench(framework); 92 | sbench(framework); 93 | // cellxBench(framework); fail!! 94 | await dynamicBench(framework); 95 | } 96 | -------------------------------------------------------------------------------- /frameworks/riverpod/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | collection: 5 | dependency: transitive 6 | description: 7 | name: collection 8 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.19.1" 12 | meta: 13 | dependency: transitive 14 | description: 15 | name: meta 16 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.16.0" 20 | path: 21 | dependency: transitive 22 | description: 23 | name: path 24 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.9.1" 28 | reactivity_benchmark: 29 | dependency: "direct main" 30 | description: 31 | path: "../.." 32 | relative: true 33 | source: path 34 | version: "0.0.1" 35 | riverpod: 36 | dependency: "direct main" 37 | description: 38 | name: riverpod 39 | sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" 40 | url: "https://pub.dev" 41 | source: hosted 42 | version: "2.6.1" 43 | stack_trace: 44 | dependency: transitive 45 | description: 46 | name: stack_trace 47 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 48 | url: "https://pub.dev" 49 | source: hosted 50 | version: "1.12.1" 51 | state_notifier: 52 | dependency: transitive 53 | description: 54 | name: state_notifier 55 | sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb 56 | url: "https://pub.dev" 57 | source: hosted 58 | version: "1.0.0" 59 | sdks: 60 | dart: ">=3.6.0 <4.0.0" 61 | -------------------------------------------------------------------------------- /frameworks/riverpod/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | riverpod: ^2.6.1 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/signals_core/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 2 | import 'package:signals_core/signals_core.dart' as signals_core; 3 | import 'package:reactivity_benchmark/reactive_framework.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class _SignalsReactiveFramework extends ReactiveFramework { 8 | const _SignalsReactiveFramework() 9 | : super('[signals](https://github.com/rodydavis/signals.dart)'); 10 | 11 | @override 12 | Computed computed(T Function() fn) { 13 | final inner = signals_core.computed(fn); 14 | return createComputed(() => inner.value); 15 | } 16 | 17 | @override 18 | void effect(void Function() fn) { 19 | signals_core.effect(fn); 20 | } 21 | 22 | @override 23 | Signal signal(T value) { 24 | final inner = signals_core.signal(value); 25 | return createSignal( 26 | () => inner.value, 27 | (value) => inner.value = value, 28 | ); 29 | } 30 | 31 | @override 32 | void withBatch(T Function() fn) { 33 | signals_core.batch(fn); 34 | } 35 | 36 | @override 37 | T withBuild(T Function() fn) => fn(); 38 | } 39 | 40 | void main() { 41 | signals_core.SignalsObserver.instance = null; 42 | runFrameworkBench(const _SignalsReactiveFramework()); 43 | } 44 | -------------------------------------------------------------------------------- /frameworks/signals_core/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | meta: 5 | dependency: transitive 6 | description: 7 | name: meta 8 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.16.0" 12 | preact_signals: 13 | dependency: transitive 14 | description: 15 | name: preact_signals 16 | sha256: a5b445796a02244b85fa43d79a7030fbfbbb08f7c48277ee93c636140a8fb2e0 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.8.3" 20 | reactivity_benchmark: 21 | dependency: "direct main" 22 | description: 23 | path: "../.." 24 | relative: true 25 | source: path 26 | version: "0.0.1" 27 | signals_core: 28 | dependency: "direct main" 29 | description: 30 | name: signals_core 31 | sha256: "5668ff1fb953fe48c88b03d50d43c5fe128cd7c831b932a4157f5f6be868b80f" 32 | url: "https://pub.dev" 33 | source: hosted 34 | version: "6.0.2" 35 | sdks: 36 | dart: ">=3.6.0 <4.0.0" 37 | -------------------------------------------------------------------------------- /frameworks/signals_core/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | signals_core: ^6.0.2 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/solidart/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 2 | import 'package:solidart/solidart.dart' as solidart; 3 | import 'package:reactivity_benchmark/reactive_framework.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class _SolidartReactiveFramework extends ReactiveFramework { 8 | const _SolidartReactiveFramework() 9 | : super( 10 | '[solidart(2.0-dev)](https://github.com/nank1ro/solidart/tree/dev)'); 11 | 12 | @override 13 | Computed computed(T Function() fn) { 14 | final computed = solidart.Computed(fn); 15 | return createComputed(() => computed.value); 16 | } 17 | 18 | @override 19 | void effect(void Function() fn) { 20 | solidart.Effect(fn); 21 | } 22 | 23 | @override 24 | Signal signal(T value) { 25 | final signal = solidart.Signal(value); 26 | return createSignal( 27 | () => signal.value, 28 | (value) => signal.value = value, 29 | ); 30 | } 31 | 32 | @override 33 | void withBatch(T Function() fn) { 34 | solidart.batch(fn); 35 | } 36 | 37 | @override 38 | T withBuild(T Function() fn) => fn(); 39 | } 40 | 41 | void main() { 42 | solidart.SolidartConfig.devToolsEnabled = false; 43 | solidart.SolidartConfig.trackPreviousValue = false; 44 | solidart.SolidartConfig.autoDispose = false; 45 | solidart.SolidartConfig.equals = true; 46 | runFrameworkBench(const _SolidartReactiveFramework()); 47 | } 48 | -------------------------------------------------------------------------------- /frameworks/solidart/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | alien_signals: 5 | dependency: transitive 6 | description: 7 | name: alien_signals 8 | sha256: f76d6e0bd59d2fa8a6ecbff8f784f04f784068fb70886ec0e21e1280624cd9d7 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "0.2.4" 12 | collection: 13 | dependency: transitive 14 | description: 15 | name: collection 16 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.19.1" 20 | meta: 21 | dependency: transitive 22 | description: 23 | name: meta 24 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.16.0" 28 | reactivity_benchmark: 29 | dependency: "direct main" 30 | description: 31 | path: "../.." 32 | relative: true 33 | source: path 34 | version: "0.0.1" 35 | solidart: 36 | dependency: "direct main" 37 | description: 38 | name: solidart 39 | sha256: "51eca7c4bbfa8218982a2ecd8f4d055698e3c72b8a1b69628da2ea4d3d7910f7" 40 | url: "https://pub.dev" 41 | source: hosted 42 | version: "2.0.0-dev.5" 43 | sdks: 44 | dart: ">=3.6.0 <4.0.0" 45 | -------------------------------------------------------------------------------- /frameworks/solidart/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | solidart: ^2.0.0-dev.5 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/solidart_v1/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 2 | import 'package:solidart/solidart.dart' as solidart; 3 | import 'package:reactivity_benchmark/reactive_framework.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class _SolidartReactiveFramework extends ReactiveFramework { 8 | const _SolidartReactiveFramework() 9 | : super('[solidart(v1)](https://github.com/nank1ro/solidart)'); 10 | 11 | @override 12 | Computed computed(T Function() fn) { 13 | final computed = solidart.Computed(fn); 14 | return createComputed(() => computed.value); 15 | } 16 | 17 | @override 18 | void effect(void Function() fn) { 19 | solidart.Effect((_) => fn()); 20 | } 21 | 22 | @override 23 | Signal signal(T value) { 24 | final signal = solidart.Signal(value); 25 | return createSignal( 26 | () => signal.value, 27 | (value) => signal.value = value, 28 | ); 29 | } 30 | 31 | @override 32 | void withBatch(T Function() fn) { 33 | fn(); 34 | } 35 | 36 | @override 37 | T withBuild(T Function() fn) => fn(); 38 | } 39 | 40 | void main() { 41 | solidart.SolidartConfig.devToolsEnabled = false; 42 | solidart.SolidartConfig.autoDispose = false; 43 | runFrameworkBench(const _SolidartReactiveFramework()); 44 | } 45 | -------------------------------------------------------------------------------- /frameworks/solidart_v1/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | collection: 5 | dependency: transitive 6 | description: 7 | name: collection 8 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.19.1" 12 | meta: 13 | dependency: transitive 14 | description: 15 | name: meta 16 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.16.0" 20 | reactivity_benchmark: 21 | dependency: "direct main" 22 | description: 23 | path: "../.." 24 | relative: true 25 | source: path 26 | version: "0.0.1" 27 | solidart: 28 | dependency: "direct main" 29 | description: 30 | name: solidart 31 | sha256: "2d15608b60fff6cf1ed39b4161bde03caddacf6656275d3bedeb3605edfc60e9" 32 | url: "https://pub.dev" 33 | source: hosted 34 | version: "1.5.4" 35 | sdks: 36 | dart: ">=3.6.0 <4.0.0" 37 | -------------------------------------------------------------------------------- /frameworks/solidart_v1/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | solidart: ^1.5.4 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /frameworks/state_beacon/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:state_beacon_core/state_beacon_core.dart' as state_beacon; 2 | import 'package:reactivity_benchmark/run_framework_bench.dart'; 3 | import 'package:reactivity_benchmark/reactive_framework.dart'; 4 | import 'package:reactivity_benchmark/utils/create_computed.dart'; 5 | import 'package:reactivity_benchmark/utils/create_signal.dart'; 6 | 7 | final class _StateBeaconReactiveFramework extends ReactiveFramework { 8 | const _StateBeaconReactiveFramework() 9 | : super('[state_beacon](https://github.com/jinyus/dart_beacon)'); 10 | 11 | @override 12 | Computed computed(T Function() fn) { 13 | final inner = state_beacon.Beacon.derived(fn); 14 | return createComputed(() => inner.value); 15 | } 16 | 17 | @override 18 | void effect(void Function() fn) { 19 | state_beacon.Beacon.effect(() => fn()); 20 | } 21 | 22 | @override 23 | Signal signal(T value) { 24 | final signal = state_beacon.Beacon.writable(value); 25 | return createSignal(() => signal.value, signal.set); 26 | } 27 | 28 | @override 29 | void withBatch(T Function() fn) { 30 | fn(); 31 | } 32 | 33 | @override 34 | T withBuild(T Function() fn) => fn(); 35 | } 36 | 37 | void main() { 38 | runFrameworkBench(const _StateBeaconReactiveFramework()); 39 | } 40 | -------------------------------------------------------------------------------- /frameworks/state_beacon/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | basic_interfaces: 5 | dependency: transitive 6 | description: 7 | name: basic_interfaces 8 | sha256: c37c8c4ddbc594430eb3817a4edfcc9a0cadbc7ea4c51a5b48785e351f1ba479 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "1.0.4" 12 | reactivity_benchmark: 13 | dependency: "direct main" 14 | description: 15 | path: "../.." 16 | relative: true 17 | source: path 18 | version: "0.0.1" 19 | state_beacon_core: 20 | dependency: "direct main" 21 | description: 22 | name: state_beacon_core 23 | sha256: c109a5fee4b93f1cf2fcb2e6ea9c74285654e3e8799eb6e33c69581807214f94 24 | url: "https://pub.dev" 25 | source: hosted 26 | version: "1.0.0" 27 | sdks: 28 | dart: ">=3.6.0 <4.0.0" 29 | -------------------------------------------------------------------------------- /frameworks/state_beacon/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _ 2 | environment: 3 | sdk: ^3.6.0 4 | dependencies: 5 | state_beacon_core: ^1.0.0 6 | reactivity_benchmark: 7 | path: ../../ 8 | -------------------------------------------------------------------------------- /gen_bench_report.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | Future main() async { 4 | final reports = readAllBenchmarkReports(); 5 | 6 | // 1. Generate detailed test case table 7 | generateTestCaseTable(reports); 8 | 9 | // 2. Calculate overall rank 10 | final ranks = calculateRank(reports); 11 | 12 | // 3. Generate ranking report 13 | generateRankingReport(ranks); 14 | } 15 | 16 | class RankResult { 17 | final String framework; 18 | final double successRate; 19 | final int totalTests; 20 | final int passedTests; 21 | final Duration totalTime; 22 | 23 | RankResult({ 24 | required this.framework, 25 | required this.successRate, 26 | required this.totalTests, 27 | required this.passedTests, 28 | required this.totalTime, 29 | }); 30 | } 31 | 32 | Map> 33 | readAllBenchmarkReports() { 34 | final reports = >{}; 35 | final benchReportDir = Directory('bench'); 36 | 37 | for (final file in benchReportDir.listSync()) { 38 | if (file is! File) continue; 39 | final lines = file.readAsLinesSync().skip(2); 40 | for (final line in lines) { 41 | final [framework, testCase, microseconds] = 42 | line.split('|').skip(1).take(3).map((e) => e.trim()).toList(); 43 | reports[framework] ??= {}; 44 | reports[framework]![testCase] = int.parse(microseconds); 45 | } 46 | } 47 | 48 | final testCases = 49 | >{}; 50 | for (final MapEntry(key: framework, value: cases) in reports.entries) { 51 | for (final MapEntry(key: testCase, value: microseconds) in cases.entries) { 52 | final trimmedTestCase = trimTestCaseName(testCase); 53 | final group = testCases[trimmedTestCase] ??= 54 | {}; 55 | group[framework] = (stateCaseName: testCase, microseconds: microseconds); 56 | } 57 | } 58 | 59 | return testCases; 60 | } 61 | 62 | void generateTestCaseTable( 63 | Map> 64 | reports) { 65 | final testCaseTable = StringBuffer(); 66 | 67 | // 获取所有框架并排序,确保顺序一致 68 | final frameworks = 69 | reports.values.expand((group) => group.keys).toSet().toList()..sort(); 70 | 71 | testCaseTable.write('| Test Case | '); 72 | testCaseTable.write(frameworks.join(' | ')); 73 | testCaseTable.writeln(' |'); 74 | 75 | testCaseTable.write('|---|'); 76 | testCaseTable 77 | .write(Iterable.generate(frameworks.length, (_) => '---').join('|')); 78 | testCaseTable.writeln('|'); 79 | 80 | for (final MapEntry(key: testCase, value: group) in reports.entries) { 81 | testCaseTable.write('| $testCase | '); 82 | testCaseTable.writeAll([ 83 | for (final framework in frameworks) 84 | if (group.containsKey(framework)) 85 | formatTestResult( 86 | group[framework]!.microseconds, group[framework]!.stateCaseName) 87 | else 88 | 'N/A', 89 | ], ' | '); 90 | testCaseTable.writeln(' |'); 91 | } 92 | 93 | updateReadme(testCaseTable.toString(), 'test-case'); 94 | } 95 | 96 | double calculateCoefficient(String testCase) { 97 | if (testCase.contains('(fail)')) return 0.0; 98 | 99 | double coefficient = 1.0; 100 | if (testCase.contains('sum: fail')) coefficient -= 0.5; 101 | if (testCase.contains('count: fail')) coefficient -= 0.5; 102 | 103 | return coefficient; 104 | } 105 | 106 | String getTestStatus(double coefficient) { 107 | if (coefficient >= 1.0) return 'pass'; 108 | if (coefficient > 0.0) return 'partial'; 109 | return 'fail'; 110 | } 111 | 112 | String formatDuration(Duration duration) { 113 | final microseconds = duration.inMicroseconds; 114 | if (microseconds < 1000) { 115 | return '$microsecondsμs'; 116 | } else if (microseconds < 1000000) { 117 | return '${(microseconds / 1000).toStringAsFixed(2)}ms'; 118 | } 119 | return '${(microseconds / 1000000).toStringAsFixed(2)}s'; 120 | } 121 | 122 | String formatTestResult(int microseconds, String testCase) { 123 | final coefficient = calculateCoefficient(testCase); 124 | final time = formatDuration(Duration(microseconds: microseconds)); 125 | 126 | if (coefficient == 0.0) return '$time (fail)'; 127 | if (coefficient == 0.5) return '$time (partial)'; 128 | return time; 129 | } 130 | 131 | void updateReadme(String content, String section) { 132 | final readme = File('README.md'); 133 | final readmeContent = readme.readAsStringSync(); 134 | 135 | final sectionStart = readmeContent.indexOf('') + 136 | ''.length; 137 | final sectionEnd = readmeContent.indexOf(''); 138 | 139 | final newContent = 140 | readmeContent.replaceRange(sectionStart, sectionEnd, '\n$content\n'); 141 | 142 | readme.writeAsStringSync(newContent); 143 | } 144 | 145 | String trimTestCaseName(String name) { 146 | const needRemoved = [ 147 | '(success)', 148 | '(fail)', 149 | '(first: pass,', 150 | '(first: fail,', 151 | 'last: pass)', 152 | 'last: fail)', 153 | ', sum: pass', 154 | ', sum: fail', 155 | ', count: pass', 156 | ', count: fail', 157 | ]; 158 | 159 | for (final pattern in needRemoved) { 160 | name = name.replaceAll(pattern, '').trim(); 161 | } 162 | 163 | return name; 164 | } 165 | 166 | Map calculateRank( 167 | Map> 168 | reports) { 169 | final results = {}; 170 | 171 | // Second pass to calculate scores 172 | for (final framework in reports.values.first.keys) { 173 | var totalTests = 0; 174 | var passedTests = 0; 175 | var totalTime = Duration.zero; 176 | 177 | for (final MapEntry(value: group) in reports.entries) { 178 | final test = group[framework]; 179 | totalTests++; 180 | if (test == null) continue; 181 | 182 | final coefficient = calculateCoefficient(test.stateCaseName); 183 | if (coefficient >= 1.0) passedTests++; 184 | 185 | totalTime += Duration(microseconds: test.microseconds); 186 | } 187 | 188 | final successRate = passedTests / totalTests; 189 | 190 | results[framework] = RankResult( 191 | framework: framework, 192 | successRate: successRate, 193 | totalTests: totalTests, 194 | passedTests: passedTests, 195 | totalTime: totalTime, 196 | ); 197 | } 198 | 199 | return results; 200 | } 201 | 202 | void generateRankingReport(Map scores) { 203 | final rankTable = StringBuffer(); 204 | rankTable.writeln('| Rank | Framework | Success Rate | Tests | Time |'); 205 | rankTable.writeln('|------|-----------|--------------|-------|------|'); 206 | 207 | final successFrameworks = scores.values 208 | .where((e) => e.successRate == 1) 209 | .toList() 210 | ..sort((a, b) => a.totalTime.inMicroseconds - b.totalTime.inMicroseconds); 211 | 212 | for (final (index, result) in successFrameworks.indexed) { 213 | final rank = switch (index) { 214 | 0 => '🥇', 215 | 1 => '🥈', 216 | 2 => '🥉', 217 | _ => '${index + 1}', 218 | }; 219 | 220 | rankTable.writeln('| $rank | ${result.framework} | ' 221 | '${(result.successRate * 100).toStringAsFixed(1)}% | ' 222 | '${result.passedTests}/${result.totalTests} | ' 223 | '${formatDuration(result.totalTime)} |'); 224 | } 225 | 226 | updateReadme(rankTable.toString(), 'ranking'); 227 | 228 | final failTable = StringBuffer(); 229 | failTable.writeln('| Framework | Success Rate | Tests | Time |'); 230 | failTable.writeln('|-----------|--------------|-------|------|'); 231 | 232 | final failedFrameworks = scores.values 233 | .where((e) => e.successRate < 1) 234 | .toList() 235 | ..sort((a, b) => a.totalTime.inMicroseconds - b.totalTime.inMicroseconds); 236 | 237 | for (final result in failedFrameworks) { 238 | failTable.writeln('| ${result.framework} | ' 239 | '${(result.successRate * 100).toStringAsFixed(1)}% | ' 240 | '${result.passedTests}/${result.totalTests} | ' 241 | '${formatDuration(result.totalTime)} |'); 242 | } 243 | 244 | updateReadme(failTable.toString(), 'fail'); 245 | } 246 | -------------------------------------------------------------------------------- /lib/cellx_bench.dart: -------------------------------------------------------------------------------- 1 | import 'reactive_framework.dart'; 2 | import 'utils/perf_logging.dart'; 3 | 4 | (int, List, List) _cellx(ReactiveFramework framework, int layers) { 5 | return framework.withBuild(() { 6 | final start = ( 7 | prop1: framework.signal(1), 8 | prop2: framework.signal(2), 9 | prop3: framework.signal(3), 10 | prop4: framework.signal(4), 11 | ); 12 | 13 | ({ 14 | ISignal prop1, 15 | ISignal prop2, 16 | ISignal prop3, 17 | ISignal prop4, 18 | }) layer = start; 19 | for (int i = layers; i > 0; i--) { 20 | final m = layer; 21 | final s = ( 22 | prop1: framework.computed(() => m.prop2.read()), 23 | prop2: framework.computed(() => m.prop1.read() - m.prop3.read()), 24 | prop3: framework.computed(() => m.prop2.read() + m.prop4.read()), 25 | prop4: framework.computed(() => m.prop3.read()), 26 | ); 27 | 28 | framework.effect(() => s.prop1.read()); 29 | framework.effect(() => s.prop2.read()); 30 | framework.effect(() => s.prop3.read()); 31 | framework.effect(() => s.prop4.read()); 32 | 33 | s.prop1.read(); 34 | s.prop2.read(); 35 | s.prop3.read(); 36 | s.prop4.read(); 37 | 38 | layer = s; 39 | } 40 | 41 | final end = layer; 42 | final stopwatch = Stopwatch()..start(); 43 | final before = [ 44 | end.prop1.read(), 45 | end.prop2.read(), 46 | end.prop3.read(), 47 | end.prop4.read(), 48 | ]; 49 | 50 | framework.withBatch(() { 51 | start.prop1.write(4); 52 | start.prop2.write(3); 53 | start.prop3.write(2); 54 | start.prop4.write(1); 55 | }); 56 | 57 | final after = [ 58 | end.prop1.read(), 59 | end.prop2.read(), 60 | end.prop3.read(), 61 | end.prop4.read(), 62 | ]; 63 | 64 | stopwatch.stop(); 65 | 66 | return (stopwatch.elapsedMicroseconds, before, after); 67 | }); 68 | } 69 | 70 | typedef _BenchmarkResults = (List, List); 71 | 72 | void cellxBench(ReactiveFramework framework) { 73 | const expected = { 74 | 1000: ([-3, -6, -2, 2], [-2, -4, 2, 3]), 75 | 2500: ([-3, -6, -2, 2], [-2, -4, 2, 3]), 76 | 5000: ([2, 4, -1, -6], [-2, 1, -4, -4]), 77 | }; 78 | 79 | final results = {}; 80 | for (final layers in expected.keys) { 81 | int total = 0; 82 | for (int i = 0; i < 10; i++) { 83 | final (elapsed, before, after) = _cellx(framework, layers); 84 | results[layers] = (before, after); 85 | total += elapsed; 86 | } 87 | 88 | final (before, after) = results[layers]!; 89 | final (expectedBefore, expectedAfter) = expected[layers]!; 90 | final first = 91 | _listEqual(before, expectedBefore) ? 'first: pass' : 'first: fail'; 92 | final last = _listEqual(after, expectedAfter) ? 'last: pass' : 'last: fail'; 93 | 94 | logPerfResult(PerfRowStrings( 95 | framework: framework.name, 96 | test: 'cellx$layers ($first, $last)', 97 | time: total.toString(), 98 | )); 99 | } 100 | } 101 | 102 | bool _listEqual(List a, List b) { 103 | if (a.length != b.length) return false; 104 | for (final (i, v) in a.indexed) { 105 | if (v != b[i]) return false; 106 | } 107 | return true; 108 | } 109 | -------------------------------------------------------------------------------- /lib/config.dart: -------------------------------------------------------------------------------- 1 | import 'framework_type.dart'; 2 | import 'utils/perf_tests.dart'; 3 | 4 | const perfTests = [ 5 | TestConfig( 6 | name: 'simple', 7 | width: 10, 8 | staticFraction: 1, 9 | nSources: 2, 10 | totalLayers: 5, 11 | readFraction: 0.2, 12 | iterations: 600000, 13 | expected: TestResult(sum: 19199828, count: 3180010), 14 | ), 15 | TestConfig( 16 | name: 'dynamic', 17 | width: 10, 18 | staticFraction: 3 / 4, 19 | nSources: 6, 20 | totalLayers: 10, 21 | readFraction: 0.2, 22 | iterations: 15000, 23 | expected: TestResult(sum: 302310477860, count: 1140004), 24 | ), 25 | TestConfig( 26 | name: 'large', 27 | width: 1000, 28 | staticFraction: 0.95, 29 | nSources: 4, 30 | totalLayers: 12, 31 | readFraction: 1, 32 | iterations: 7000, 33 | expected: TestResult(sum: 29355933696000, count: 1473789), 34 | ), 35 | TestConfig( 36 | name: 'wide dense', 37 | width: 1000, 38 | totalLayers: 5, 39 | staticFraction: 1, 40 | nSources: 25, 41 | readFraction: 1, 42 | iterations: 3000, 43 | expected: TestResult(sum: 1171484375000, count: 735756), 44 | ), 45 | TestConfig( 46 | name: 'deep', 47 | width: 5, 48 | totalLayers: 500, 49 | staticFraction: 1, 50 | nSources: 3, 51 | readFraction: 1, 52 | iterations: 500, 53 | expected: TestResult(sum: 6329683023313797861, count: 1246502), 54 | ), 55 | TestConfig( 56 | name: 'very dynamic', 57 | width: 100, 58 | totalLayers: 15, 59 | staticFraction: 0.5, 60 | nSources: 6, 61 | readFraction: 1, 62 | iterations: 2000, 63 | expected: TestResult(sum: 15664996402790400, count: 1078734), 64 | ), 65 | ]; 66 | -------------------------------------------------------------------------------- /lib/dynamic_bench.dart: -------------------------------------------------------------------------------- 1 | import 'config.dart'; 2 | import 'framework_type.dart'; 3 | import 'reactive_framework.dart'; 4 | import 'utils/bench_repeat.dart'; 5 | import 'utils/dep_graph.dart'; 6 | import 'utils/perf_logging.dart'; 7 | import 'utils/perf_tests.dart'; 8 | 9 | Future dynamicBench(ReactiveFramework framework, 10 | {int testRepeats = 1}) async { 11 | for (final config in perfTests) { 12 | final TestConfig(:iterations, :readFraction) = config; 13 | final counter = Counter(); 14 | int runOnce() { 15 | try { 16 | final graph = makeGraph(framework, config, counter); 17 | return runGraph(graph, iterations, readFraction, framework); 18 | } catch (e) { 19 | return -1; 20 | } 21 | } 22 | 23 | // warm up 24 | runOnce(); 25 | 26 | final timingResult = await fastestTest(testRepeats, () { 27 | counter.count = 0; 28 | final sum = runOnce(); 29 | return TestResult(sum: sum, count: counter.count); 30 | }); 31 | 32 | logPerfResult(perfRowStrings(framework.name, config, timingResult)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/framework_type.dart: -------------------------------------------------------------------------------- 1 | import 'utils/perf_tests.dart'; 2 | 3 | class TestConfig { 4 | const TestConfig({ 5 | this.name, 6 | required this.width, 7 | required this.totalLayers, 8 | required this.staticFraction, 9 | required this.nSources, 10 | required this.readFraction, 11 | required this.iterations, 12 | required this.expected, 13 | }); 14 | 15 | final String? name; 16 | 17 | final int width; 18 | final int totalLayers; 19 | final double staticFraction; 20 | final int nSources; 21 | final double readFraction; 22 | final int iterations; 23 | final TestResult expected; 24 | } 25 | -------------------------------------------------------------------------------- /lib/kairo/avoidable.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() avoidablePropagation(ReactiveFramework f) { 6 | final head = f.signal(0); 7 | final c1 = f.computed(() => head.read()); 8 | final c2 = f.computed(() { 9 | c1.read(); 10 | return 0; 11 | }); 12 | final c3 = f.computed(() { 13 | busy(); 14 | return c2.read() + 1; 15 | }); 16 | final c4 = f.computed(() => c3.read() + 2); 17 | final c5 = f.computed(() => c4.read() + 3); 18 | 19 | final counter = Counter(); 20 | f.effect(() { 21 | counter.count++; 22 | c5.read(); 23 | busy(); 24 | }); 25 | 26 | return () { 27 | KairoState state = KairoState.success; 28 | f.withBatch(() => head.write(1)); 29 | if (c5.read() != 6) { 30 | state = KairoState.fail; 31 | } 32 | 33 | for (int i = 0; i < 1000; i++) { 34 | f.withBatch(() => head.write(i)); 35 | if (c5.read() != 6) { 36 | state = KairoState.fail; 37 | } 38 | } 39 | 40 | if (counter.count != 1) { 41 | return KairoState.fail; 42 | } 43 | 44 | return state; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /lib/kairo/broad.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() broadPropagation(ReactiveFramework framework) { 6 | final head = framework.signal(0); 7 | ISignal last = head; 8 | final callCounter = Counter(); 9 | 10 | for (int i = 0; i < 50; i++) { 11 | final current = framework.computed(() { 12 | return head.read() + i; 13 | }); 14 | 15 | final current2 = framework.computed(() { 16 | return current.read() + 1; 17 | }); 18 | 19 | framework.effect(() { 20 | current2.read(); 21 | callCounter.count++; 22 | }); 23 | 24 | last = current2; 25 | } 26 | 27 | return () { 28 | KairoState state = KairoState.success; 29 | 30 | framework.withBatch(() { 31 | head.write(1); 32 | }); 33 | 34 | callCounter.count = 0; 35 | for (int i = 0; i < 50; i++) { 36 | framework.withBatch(() { 37 | head.write(i); 38 | }); 39 | 40 | final count = (i + 1) * 50; 41 | if (last.read() != i + 50 || callCounter.count != count) { 42 | state = KairoState.fail; 43 | } 44 | } 45 | 46 | return state; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /lib/kairo/deep.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() deepPropagation(ReactiveFramework framework) { 6 | const len = 50; 7 | 8 | return framework.withBuild(() { 9 | final head = framework.signal(0); 10 | var current = head as ISignal; 11 | 12 | for (int i = 0; i < len; i++) { 13 | final c = current; 14 | current = framework.computed(() => c.read() + 1); 15 | } 16 | 17 | final callCounter = Counter(); 18 | framework.effect(() { 19 | current.read(); 20 | callCounter.count++; 21 | }); 22 | 23 | const iter = 50; 24 | return () { 25 | framework.withBatch(() { 26 | head.write(1); 27 | }); 28 | 29 | KairoState state = KairoState.success; 30 | 31 | callCounter.count = 0; 32 | for (int i = 0; i < iter; i++) { 33 | framework.withBatch(() { 34 | head.write(i); 35 | }); 36 | if (current.read() != len + i) { 37 | state = KairoState.fail; 38 | } 39 | } 40 | 41 | if (callCounter.count != iter) { 42 | return KairoState.fail; 43 | } 44 | 45 | return state; 46 | }; 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /lib/kairo/diamond.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() diamond(ReactiveFramework framework) { 6 | const width = 5; 7 | 8 | return framework.withBuild(() { 9 | final head = framework.signal(0); 10 | final current = >[]; 11 | for (int i = 0; i < width; i++) { 12 | current.add( 13 | framework.computed(() => head.read() + 1), 14 | ); 15 | } 16 | 17 | final sum = framework.computed(() { 18 | return current.fold(0, (prev, x) => prev + x.read()); 19 | }); 20 | 21 | final callCounter = Counter(); 22 | framework.effect(() { 23 | sum.read(); 24 | callCounter.count++; 25 | }); 26 | 27 | return () { 28 | KairoState state = KairoState.success; 29 | framework.withBatch(() { 30 | head.write(1); 31 | }); 32 | if (sum.read() != 2 * width) { 33 | state = KairoState.fail; 34 | } 35 | 36 | callCounter.count = 0; 37 | for (int i = 0; i < 500; i++) { 38 | framework.withBatch(() { 39 | head.write(i); 40 | }); 41 | assert(sum.read() == (i + 1) * width); 42 | if (sum.read() != (i + 1) * width) { 43 | state = KairoState.fail; 44 | } 45 | } 46 | 47 | if (callCounter.count != 500) { 48 | return KairoState.fail; 49 | } 50 | 51 | return state; 52 | }; 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /lib/kairo/mux.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import 'utils.dart'; 3 | 4 | KairoState Function() mux(ReactiveFramework framework) { 5 | return framework.withBuild(() { 6 | final heads = List.generate(100, (_) => framework.signal(0)); 7 | final mux = framework.computed(() { 8 | return Map.fromEntries( 9 | heads.map((h) => h.read()).toList().asMap().entries, 10 | ); 11 | }); 12 | 13 | final splited = heads 14 | .asMap() 15 | .entries 16 | .map((e) => framework.computed(() => mux.read()[e.key]!)) 17 | .map((x) => framework.computed(() => x.read() + 1)) 18 | .toList(); 19 | 20 | int sum = 0; 21 | for (final x in splited) { 22 | framework.effect(() => sum += x.read()); 23 | } 24 | 25 | return () { 26 | KairoState state = KairoState.success; 27 | sum = 0; 28 | 29 | for (int i = 0; i < 10; i++) { 30 | framework.withBatch(() { 31 | heads[i].write(i); 32 | }); 33 | if (splited[i].read() != i + 1) { 34 | state = KairoState.fail; 35 | } 36 | } 37 | 38 | state = sum != 54 ? KairoState.fail : state; 39 | sum = 0; 40 | 41 | for (int i = 0; i < 10; i++) { 42 | framework.withBatch(() { 43 | heads[i].write(i * 2); 44 | }); 45 | 46 | if (splited[i].read() != i * 2 + 1) { 47 | state = KairoState.fail; 48 | } 49 | } 50 | 51 | if (sum != 99) { 52 | return KairoState.fail; 53 | } 54 | 55 | return state; 56 | }; 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /lib/kairo/repeated.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() repeatedObservers(ReactiveFramework framework) { 6 | const size = 30; 7 | 8 | return framework.withBuild(() { 9 | final head = framework.signal(0); 10 | final current = framework.computed(() { 11 | int result = 0; 12 | for (int i = 0; i < size; i++) { 13 | result += head.read(); 14 | } 15 | return result; 16 | }); 17 | 18 | final callCounter = Counter(); 19 | framework.effect(() { 20 | current.read(); 21 | callCounter.count++; 22 | }); 23 | 24 | return () { 25 | framework.withBatch(() { 26 | head.write(1); 27 | }); 28 | 29 | KairoState state = 30 | current.read() == size ? KairoState.success : KairoState.fail; 31 | 32 | callCounter.count = 0; 33 | for (int i = 0; i < 100; i++) { 34 | framework.withBatch(() { 35 | head.write(i); 36 | }); 37 | 38 | if (current.read() != i * size) { 39 | state = KairoState.fail; 40 | } 41 | } 42 | 43 | if (callCounter.count != 100) { 44 | return KairoState.fail; 45 | } 46 | 47 | return state; 48 | }; 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /lib/kairo/triangle.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() triangle(ReactiveFramework framework) { 6 | const width = 10; 7 | return framework.withBuild(() { 8 | final head = framework.signal(0); 9 | var current = head as ISignal; 10 | final list = >[]; 11 | 12 | for (int i = 0; i < width; i++) { 13 | final c = current; 14 | list.add(current); 15 | current = framework.computed(() { 16 | return c.read() + 1; 17 | }); 18 | } 19 | 20 | final sum = framework.computed(() { 21 | return list.map((x) => x.read()).reduce((a, b) => a + b); 22 | }); 23 | 24 | final callCounter = Counter(); 25 | framework.effect(() { 26 | sum.read(); 27 | callCounter.count++; 28 | }); 29 | 30 | return () { 31 | KairoState state = KairoState.success; 32 | final constant = _count(width); 33 | framework.withBatch(() { 34 | head.write(1); 35 | }); 36 | 37 | if (sum.read() != constant) { 38 | state = KairoState.fail; 39 | } 40 | 41 | callCounter.count = 0; 42 | for (int i = 0; i < 100; i++) { 43 | framework.withBatch(() { 44 | head.write(i); 45 | }); 46 | 47 | if (sum.read() != constant - width + i * width) { 48 | state = KairoState.fail; 49 | } 50 | } 51 | 52 | if (callCounter.count != 100) { 53 | return KairoState.fail; 54 | } 55 | 56 | return state; 57 | }; 58 | }); 59 | } 60 | 61 | int _count(int number) { 62 | return List.generate(number, (i) => i + 1).reduce((x, y) => x + y); 63 | } 64 | -------------------------------------------------------------------------------- /lib/kairo/unstable.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | import '../utils/dep_graph.dart'; 3 | import 'utils.dart'; 4 | 5 | KairoState Function() unstable(ReactiveFramework framework) { 6 | return framework.withBuild(() { 7 | final head = framework.signal(0); 8 | final double = framework.computed(() => head.read() * 2); 9 | final inverse = framework.computed(() => -head.read()); 10 | final current = framework.computed(() { 11 | var result = 0; 12 | for (int i = 0; i < 20; i++) { 13 | result += head.read() % 2 == 1 ? double.read() : inverse.read(); 14 | } 15 | return result; 16 | }); 17 | 18 | final callCounter = Counter(); 19 | framework.effect(() { 20 | current.read(); 21 | callCounter.count++; 22 | }); 23 | 24 | return () { 25 | framework.withBatch(() { 26 | head.write(1); 27 | }); 28 | KairoState state = 29 | current.read() == 40 ? KairoState.success : KairoState.fail; 30 | callCounter.count = 0; 31 | 32 | for (int i = 0; i < 100; i++) { 33 | framework.withBatch(() { 34 | head.write(i); 35 | }); 36 | 37 | if (current.read() != (i % 2 == 1 ? i * 2 : -i) * 20) { 38 | return KairoState.fail; 39 | } 40 | } 41 | 42 | if (callCounter.count != 100) { 43 | return KairoState.fail; 44 | } 45 | 46 | return state; 47 | }; 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /lib/kairo/utils.dart: -------------------------------------------------------------------------------- 1 | void busy() { 2 | int a = 0; 3 | for (int i = 0; i < 1_00; i++) { 4 | a++; 5 | } 6 | a; 7 | } 8 | 9 | enum KairoState { 10 | success, 11 | fail, 12 | } 13 | -------------------------------------------------------------------------------- /lib/kairo_bench.dart: -------------------------------------------------------------------------------- 1 | import 'kairo/utils.dart'; 2 | import 'utils/bench_repeat.dart'; 3 | import 'utils/perf_logging.dart'; 4 | import 'reactive_framework.dart'; 5 | import 'kairo/avoidable.dart'; 6 | import 'kairo/broad.dart'; 7 | import 'kairo/deep.dart'; 8 | import 'kairo/diamond.dart'; 9 | import 'kairo/mux.dart'; 10 | import 'kairo/repeated.dart'; 11 | import 'kairo/triangle.dart'; 12 | import 'kairo/unstable.dart'; 13 | 14 | final cases = [ 15 | (avoidablePropagation, 'avoidablePropagation'), 16 | (broadPropagation, 'broadPropagation'), 17 | (deepPropagation, 'deepPropagation'), 18 | (diamond, 'diamond'), 19 | (mux, 'mux'), 20 | (repeatedObservers, 'repeatedObservers'), 21 | (triangle, 'triangle'), 22 | (unstable, 'unstable'), 23 | ]; 24 | 25 | Future kairoBench(ReactiveFramework framework) async { 26 | for (final (testCase, name) in cases) { 27 | final iter = framework.withBuild(() { 28 | final iter = testCase(framework); 29 | return iter; 30 | }); 31 | 32 | // warm up 33 | KairoState state = iter(); 34 | 35 | final timingResult = await fastestTest(10, () { 36 | for (int i = 0; i < 1000; i++) { 37 | final itemState = iter(); 38 | if (state == KairoState.success) { 39 | state = itemState; 40 | } 41 | } 42 | return null; 43 | }); 44 | 45 | logPerfResult(PerfRowStrings( 46 | framework: framework.name, 47 | test: '$name (${state.name})', 48 | time: timingResult.timing.time.toString(), 49 | )); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/mol_bench.dart: -------------------------------------------------------------------------------- 1 | import 'utils/bench_repeat.dart'; 2 | import 'utils/perf_logging.dart'; 3 | import 'reactive_framework.dart'; 4 | 5 | int fib(int n) { 6 | if (n < 2) return 1; 7 | return fib(n - 1) + fib(n - 2); 8 | } 9 | 10 | int hard(int n, String _) { 11 | return n + fib(16); 12 | } 13 | 14 | final numbers = List.generate(5, (i) => i); 15 | 16 | Future molBench(ReactiveFramework framework) async { 17 | final res = []; 18 | final iter = framework.withBuild(() { 19 | final a = framework.signal(0); 20 | final b = framework.signal(0); 21 | final c = framework.computed(() => (a.read() % 2) + (b.read() % 2)); 22 | final d = framework.computed(() => numbers 23 | .map((i) => {'x': i + (a.read() % 2) - (b.read() % 2)}) 24 | .toList()); 25 | final e = framework 26 | .computed(() => hard(c.read() + a.read() + d.read()[0]['x']!, 'E')); 27 | final f = framework.computed(() => hard(d.read()[2]['x'] ?? b.read(), 'F')); 28 | final g = framework.computed(() => 29 | c.read() + (c.read() + e.read() % 2) + d.read()[4]['x']! + f.read()); 30 | 31 | framework.effect(() => res.add(hard(g.read(), 'H'))); 32 | framework.effect(() => res.add(g.read())); 33 | framework.effect(() => res.add(hard(f.read(), 'J'))); 34 | 35 | return (int i) { 36 | res.clear(); 37 | framework.withBatch(() { 38 | b.write(1); 39 | a.write(1 + i * 2); 40 | }); 41 | framework.withBatch(() { 42 | a.write(2 + i * 2); 43 | b.write(2); 44 | }); 45 | }; 46 | }); 47 | 48 | iter(1); 49 | 50 | final timingResult = await fastestTest(10, () { 51 | for (int i = 0; i < 10000; i++) { 52 | iter(i); 53 | } 54 | }); 55 | 56 | logPerfResult(PerfRowStrings( 57 | framework: framework.name, 58 | test: 'molBench', 59 | time: timingResult.timing.time.toString(), 60 | )); 61 | } 62 | -------------------------------------------------------------------------------- /lib/reactive_framework.dart: -------------------------------------------------------------------------------- 1 | abstract interface class ISignal { 2 | T read(); 3 | } 4 | 5 | abstract interface class Signal implements ISignal { 6 | void write(T value); 7 | } 8 | 9 | abstract interface class Computed implements ISignal {} 10 | 11 | abstract class ReactiveFramework { 12 | const ReactiveFramework(this.name); 13 | 14 | final String name; 15 | 16 | Signal signal(T value); 17 | Computed computed(T Function() fn); 18 | void effect(void Function() fn); 19 | void withBatch(T Function() fn); 20 | T withBuild(T Function() fn); 21 | } 22 | -------------------------------------------------------------------------------- /lib/run_framework_bench.dart: -------------------------------------------------------------------------------- 1 | import 'cellx_bench.dart'; 2 | import 'dynamic_bench.dart'; 3 | import 'kairo_bench.dart'; 4 | import 'mol_bench.dart'; 5 | import 'reactive_framework.dart'; 6 | import 's_bench.dart'; 7 | import 'utils/perf_logging.dart'; 8 | 9 | Future runFrameworkBench(ReactiveFramework framework) async { 10 | printPerfReportHeaders(); 11 | await kairoBench(framework); 12 | await molBench(framework); 13 | sbench(framework); 14 | cellxBench(framework); 15 | await dynamicBench(framework); 16 | } 17 | -------------------------------------------------------------------------------- /lib/s_bench.dart: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/solidjs/solid/blob/main/packages/solid/bench/bench.cjs 2 | import 'reactive_framework.dart'; 3 | import 'utils/perf_logging.dart'; 4 | 5 | const count = 100000; 6 | 7 | // Common type definitions 8 | typedef Reader = int Function(); 9 | typedef SignalCreator = List> Function( 10 | int n, 11 | List> sources, 12 | ReactiveFramework framework, 13 | ); 14 | 15 | typedef ComputationCreator = void Function( 16 | int n, 17 | List> sources, 18 | ReactiveFramework framework, 19 | ); 20 | 21 | typedef UpdateTest = void Function( 22 | int n, 23 | List> sources, 24 | ReactiveFramework framework, 25 | ); 26 | 27 | // Test cases organized by type 28 | final createTests = { 29 | 'create_signals': (createDataSignals, count, count), 30 | }; 31 | 32 | final computeTests = { 33 | 'comp_0to1': (createComputations0to1, count, 0), 34 | 'comp_1to1': (createComputations1to1, count, count), 35 | 'comp_2to1': (createComputations2to1, count ~/ 2, count), 36 | 'comp_4to1': (createComputations4to1, count ~/ 4, count), 37 | 'comp_1000to1': (createComputations1000to1, count ~/ 1000, count), 38 | 'comp_1to2': (createComputations1to2, count, count ~/ 2), 39 | 'comp_1to4': (createComputations1to4, count, count ~/ 4), 40 | 'comp_1to8': (createComputations1to8, count, count ~/ 8), 41 | 'comp_1to1000': (createComputations1to1000, count, count ~/ 1000), 42 | }; 43 | 44 | final updateTests = { 45 | 'update_1to1': (updateComputations1to1, count * 4, 1), 46 | 'update_2to1': (updateComputations2to1, count * 2, 2), 47 | 'update_4to1': (updateComputations4to1, count, 4), 48 | 'update_1000to1': (updateComputations1000to1, count ~/ 100, 1000), 49 | 'update_1to2': (updateComputations1to2, count * 4, 1), 50 | 'update_1to4': (updateComputations1to4, count * 4, 1), 51 | 'update_1to1000': (updateComputations1to1000, count * 4, 1), 52 | }; 53 | 54 | void sbench(ReactiveFramework framework) { 55 | // Run create tests 56 | for (final entry in createTests.entries) { 57 | final name = entry.key; 58 | final (test, testCount, sourceCount) = entry.value; 59 | final time = runCreateTest(test, testCount, sourceCount, framework); 60 | logPerfResult(PerfRowStrings( 61 | framework: framework.name, 62 | test: name, 63 | time: time.toString(), 64 | )); 65 | } 66 | 67 | // Run compute tests 68 | for (final entry in computeTests.entries) { 69 | final name = entry.key; 70 | final (test, testCount, sourceCount) = entry.value; 71 | final time = runComputeTest(test, testCount, sourceCount, framework); 72 | logPerfResult(PerfRowStrings( 73 | framework: framework.name, 74 | test: name, 75 | time: time.toString(), 76 | )); 77 | } 78 | 79 | // Run update tests 80 | for (final entry in updateTests.entries) { 81 | final name = entry.key; 82 | final (test, testCount, sourceCount) = entry.value; 83 | final time = runUpdateTest(test, testCount, sourceCount, framework); 84 | logPerfResult(PerfRowStrings( 85 | framework: framework.name, 86 | test: name, 87 | time: time.toString(), 88 | )); 89 | } 90 | } 91 | 92 | int runCreateTest( 93 | SignalCreator fn, 94 | int n, 95 | int scount, 96 | ReactiveFramework framework, 97 | ) { 98 | final stopwatch = Stopwatch(); 99 | 100 | framework.withBuild(() { 101 | var sources = >[]; 102 | // Warm up 103 | sources = fn(scount, [], framework); 104 | fn(n ~/ 100, sources, framework); 105 | sources = fn(scount, [], framework); 106 | fn(n ~/ 100, sources, framework); 107 | sources = fn(scount, [], framework); 108 | fn(n ~/ 100, sources, framework); 109 | sources = fn(scount, [], framework); 110 | 111 | stopwatch 112 | ..reset() 113 | ..start(); 114 | fn(n, sources, framework); 115 | stopwatch.stop(); 116 | }); 117 | 118 | return stopwatch.elapsedMicroseconds; 119 | } 120 | 121 | int runComputeTest( 122 | ComputationCreator fn, 123 | int n, 124 | int scount, 125 | ReactiveFramework framework, 126 | ) { 127 | final stopwatch = Stopwatch(); 128 | 129 | framework.withBuild(() { 130 | var sources = createDataSignals(scount, [], framework); 131 | // Warm up 132 | fn(n ~/ 100, sources, framework); 133 | sources = createDataSignals(scount, [], framework); 134 | fn(n ~/ 100, sources, framework); 135 | sources = createDataSignals(scount, [], framework); 136 | fn(n ~/ 100, sources, framework); 137 | sources = createDataSignals(scount, [], framework); 138 | 139 | stopwatch 140 | ..reset() 141 | ..start(); 142 | fn(n, sources, framework); 143 | stopwatch.stop(); 144 | }); 145 | 146 | return stopwatch.elapsedMicroseconds; 147 | } 148 | 149 | int runUpdateTest( 150 | UpdateTest fn, 151 | int n, 152 | int scount, 153 | ReactiveFramework framework, 154 | ) { 155 | final stopwatch = Stopwatch(); 156 | 157 | framework.withBuild(() { 158 | var sources = createDataSignals(scount, [], framework); 159 | // Warm up 160 | fn(n ~/ 100, sources, framework); 161 | sources = createDataSignals(scount, [], framework); 162 | fn(n ~/ 100, sources, framework); 163 | sources = createDataSignals(scount, [], framework); 164 | fn(n ~/ 100, sources, framework); 165 | sources = createDataSignals(scount, [], framework); 166 | 167 | stopwatch 168 | ..reset() 169 | ..start(); 170 | fn(n, sources, framework); 171 | stopwatch.stop(); 172 | }); 173 | 174 | return stopwatch.elapsedMicroseconds; 175 | } 176 | 177 | List> createDataSignals( 178 | int n, 179 | List> sources, 180 | ReactiveFramework framework, 181 | ) { 182 | for (var i = 0; i < n; i++) { 183 | sources.add(framework.signal(i)); 184 | } 185 | return sources; 186 | } 187 | 188 | void createComputations0to1( 189 | int n, 190 | List> sources, 191 | ReactiveFramework framework, 192 | ) { 193 | for (var i = 0; i < n; i++) { 194 | createComputation0(i, framework); 195 | } 196 | } 197 | 198 | void createComputations1to1000( 199 | int n, 200 | List> sources, 201 | ReactiveFramework framework, 202 | ) { 203 | for (var i = 0; i < n ~/ 1000; i++) { 204 | int Function() get = sources[i].read; 205 | for (var j = 0; j < 1000; j++) { 206 | createComputation1(get, framework); 207 | } 208 | } 209 | } 210 | 211 | void createComputations1to8( 212 | int n, 213 | List> sources, 214 | ReactiveFramework framework, 215 | ) { 216 | for (var i = 0; i < n ~/ 8; i++) { 217 | int Function() get = sources[i].read; 218 | createComputation1(get, framework); 219 | createComputation1(get, framework); 220 | createComputation1(get, framework); 221 | createComputation1(get, framework); 222 | createComputation1(get, framework); 223 | createComputation1(get, framework); 224 | createComputation1(get, framework); 225 | createComputation1(get, framework); 226 | } 227 | } 228 | 229 | void createComputations1to4( 230 | int n, 231 | List> sources, 232 | ReactiveFramework framework, 233 | ) { 234 | for (var i = 0; i < n ~/ 4; i++) { 235 | int Function() get = sources[i].read; 236 | createComputation1(get, framework); 237 | createComputation1(get, framework); 238 | createComputation1(get, framework); 239 | createComputation1(get, framework); 240 | } 241 | } 242 | 243 | void createComputations1to2( 244 | int n, 245 | List> sources, 246 | ReactiveFramework framework, 247 | ) { 248 | for (var i = 0; i < n ~/ 2; i++) { 249 | int Function() get = sources[i].read; 250 | createComputation1(get, framework); 251 | createComputation1(get, framework); 252 | } 253 | } 254 | 255 | void createComputations1to1( 256 | int n, 257 | List> sources, 258 | ReactiveFramework framework, 259 | ) { 260 | for (var i = 0; i < n; i++) { 261 | int Function() get = sources[i].read; 262 | createComputation1(get, framework); 263 | } 264 | } 265 | 266 | void createComputations2to1( 267 | int n, 268 | List> sources, 269 | ReactiveFramework framework, 270 | ) { 271 | for (var i = 0; i < n; i++) { 272 | createComputation2( 273 | sources[i * 2].read, 274 | sources[i * 2 + 1].read, 275 | framework, 276 | ); 277 | } 278 | } 279 | 280 | void createComputations4to1( 281 | int n, 282 | List> sources, 283 | ReactiveFramework framework, 284 | ) { 285 | for (var i = 0; i < n; i++) { 286 | createComputation4( 287 | sources[i * 4].read, 288 | sources[i * 4 + 1].read, 289 | sources[i * 4 + 2].read, 290 | sources[i * 4 + 3].read, 291 | framework, 292 | ); 293 | } 294 | } 295 | 296 | void createComputations1000to1( 297 | int n, 298 | List> sources, 299 | ReactiveFramework framework, 300 | ) { 301 | for (var i = 0; i < n; i++) { 302 | createComputation1000(sources, i * 1000, framework); 303 | } 304 | } 305 | 306 | void createComputation0(int i, ReactiveFramework framework) { 307 | framework.computed(() => i); 308 | } 309 | 310 | void createComputation1(Reader s1, ReactiveFramework framework) { 311 | framework.computed(() => s1()); 312 | } 313 | 314 | void createComputation2( 315 | Reader s1, 316 | Reader s2, 317 | ReactiveFramework framework, 318 | ) { 319 | framework.computed(() => s1() + s2()); 320 | } 321 | 322 | void createComputation4( 323 | Reader s1, 324 | Reader s2, 325 | Reader s3, 326 | Reader s4, 327 | ReactiveFramework framework, 328 | ) { 329 | framework.computed(() => s1() + s2() + s3() + s4()); 330 | } 331 | 332 | void createComputation1000( 333 | List> ss, 334 | int offset, 335 | ReactiveFramework framework, 336 | ) { 337 | framework.computed(() { 338 | var sum = 0; 339 | for (var i = 0; i < 1000; i++) { 340 | sum += ss[offset + i].read(); 341 | } 342 | return sum; 343 | }); 344 | } 345 | 346 | void updateComputations1to1( 347 | int n, 348 | List> sources, 349 | ReactiveFramework framework, 350 | ) { 351 | int Function() get1 = sources[0].read; 352 | void Function(int) set1 = sources[0].write; 353 | framework.computed(() => get1()); 354 | for (var i = 0; i < n; i++) { 355 | set1(i); 356 | } 357 | } 358 | 359 | void updateComputations2to1( 360 | int n, 361 | List> sources, 362 | ReactiveFramework framework, 363 | ) { 364 | int Function() get1 = sources[0].read; 365 | void Function(int) set1 = sources[0].write; 366 | int Function() get2 = sources[1].read; 367 | framework.computed(() => get1() + get2()); 368 | for (var i = 0; i < n; i++) { 369 | set1(i); 370 | } 371 | } 372 | 373 | void updateComputations4to1( 374 | int n, 375 | List> sources, 376 | ReactiveFramework framework, 377 | ) { 378 | int Function() get1 = sources[0].read; 379 | void Function(int) set1 = sources[0].write; 380 | int Function() get2 = sources[1].read; 381 | int Function() get3 = sources[2].read; 382 | int Function() get4 = sources[3].read; 383 | framework.computed(() => get1() + get2() + get3() + get4()); 384 | for (var i = 0; i < n; i++) { 385 | set1(i); 386 | } 387 | } 388 | 389 | void updateComputations1000to1( 390 | int n, 391 | List> sources, 392 | ReactiveFramework framework, 393 | ) { 394 | void Function(int) set1 = sources[0].write; 395 | framework.computed(() { 396 | var sum = 0; 397 | for (var i = 0; i < 1000; i++) { 398 | sum += sources[i].read(); 399 | } 400 | return sum; 401 | }); 402 | for (var i = 0; i < n; i++) { 403 | set1(i); 404 | } 405 | } 406 | 407 | void updateComputations1to2( 408 | int n, 409 | List> sources, 410 | ReactiveFramework framework, 411 | ) { 412 | int Function() get1 = sources[0].read; 413 | void Function(int) set1 = sources[0].write; 414 | framework.computed(() => get1()); 415 | framework.computed(() => get1()); 416 | for (var i = 0; i < n ~/ 2; i++) { 417 | set1(i); 418 | } 419 | } 420 | 421 | void updateComputations1to4( 422 | int n, 423 | List> sources, 424 | ReactiveFramework framework, 425 | ) { 426 | int Function() get1 = sources[0].read; 427 | void Function(int) set1 = sources[0].write; 428 | framework.computed(() => get1()); 429 | framework.computed(() => get1()); 430 | framework.computed(() => get1()); 431 | framework.computed(() => get1()); 432 | for (var i = 0; i < n ~/ 4; i++) { 433 | set1(i); 434 | } 435 | } 436 | 437 | void updateComputations1to1000( 438 | int n, 439 | List> sources, 440 | ReactiveFramework framework, 441 | ) { 442 | int Function() get1 = sources[0].read; 443 | void Function(int) set1 = sources[0].write; 444 | for (var i = 0; i < 1000; i++) { 445 | framework.computed(() => get1()); 446 | } 447 | for (var i = 0; i < n ~/ 1000; i++) { 448 | set1(i); 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /lib/utils/bench_repeat.dart: -------------------------------------------------------------------------------- 1 | import 'perf.dart'; 2 | import 'perf_tests.dart'; 3 | 4 | Future> fastestTest(int times, T Function() fn) async { 5 | final results = >[]; 6 | 7 | for (int i = 0; i < times; i++) { 8 | results.add(await _runTracked(fn)); 9 | } 10 | 11 | return results.reduce((a, b) => a.timing.time < b.timing.time ? a : b); 12 | } 13 | 14 | Future> _runTracked(T Function() fn) async { 15 | final TimedResult(:result, :time) = runTimed(fn); 16 | return TimingResult(result, TestTiming(time)); 17 | } 18 | -------------------------------------------------------------------------------- /lib/utils/create_computed.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | 3 | final class _Computed implements Computed { 4 | const _Computed(this.getter); 5 | 6 | final T Function() getter; 7 | 8 | @override 9 | T read() => getter(); 10 | } 11 | 12 | Computed createComputed(T Function() fn) { 13 | return _Computed(fn); 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils/create_signal.dart: -------------------------------------------------------------------------------- 1 | import '../reactive_framework.dart'; 2 | 3 | final class _Signal implements Signal { 4 | const _Signal(this.getter, this.setter); 5 | 6 | final T Function() getter; 7 | final void Function(T _) setter; 8 | 9 | @override 10 | T read() => getter(); 11 | 12 | @override 13 | void write(T value) => setter(value); 14 | } 15 | 16 | Signal createSignal(T Function() getter, void Function(T) setter) { 17 | return _Signal(getter, setter); 18 | } 19 | -------------------------------------------------------------------------------- /lib/utils/dep_graph.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import '../framework_type.dart'; 3 | import '../reactive_framework.dart'; 4 | 5 | class Graph { 6 | const Graph({ 7 | required this.sources, 8 | required this.layers, 9 | }); 10 | 11 | final List> sources; 12 | final List>> layers; 13 | } 14 | 15 | class Counter { 16 | int count = 0; 17 | } 18 | 19 | Graph makeGraph( 20 | ReactiveFramework framework, 21 | TestConfig config, 22 | Counter counter, 23 | ) { 24 | final TestConfig(:width, :totalLayers, :staticFraction, :nSources) = config; 25 | 26 | return framework.withBuild(() { 27 | final sources = List.generate(width, (i) => framework.signal(i)); 28 | final rows = _makeDependentRows( 29 | sources, 30 | totalLayers - 1, 31 | counter, 32 | staticFraction, 33 | nSources, 34 | framework, 35 | ); 36 | 37 | return Graph(sources: sources, layers: rows); 38 | }); 39 | } 40 | 41 | int runGraph( 42 | Graph graph, 43 | int iterations, 44 | double readFraction, 45 | ReactiveFramework framework, 46 | ) { 47 | final random = Random(0); 48 | final Graph(:sources, :layers) = graph; 49 | final leaves = layers.last; 50 | final skipCount = (leaves.length * (1 - readFraction)).round(); 51 | final readLeaves = _removeElems(leaves, skipCount, random); 52 | 53 | late int sum; 54 | framework.withBatch(() { 55 | for (int i = 0; i < iterations; i++) { 56 | final sourceDex = i % sources.length; 57 | sources[sourceDex].write(i + sourceDex); 58 | 59 | for (final leaf in readLeaves) { 60 | leaf.read(); 61 | } 62 | } 63 | 64 | sum = readLeaves.fold(0, (total, leaf) => total + leaf.read()); 65 | }); 66 | 67 | return sum; 68 | } 69 | 70 | List _removeElems(List src, int rmCount, Random random) { 71 | final copy = List.from(src); 72 | for (int i = 0; i < rmCount && copy.isNotEmpty; i++) { 73 | final rmDex = random.nextInt(copy.length); 74 | copy.removeAt(rmDex); 75 | } 76 | return copy; 77 | } 78 | 79 | List>> _makeDependentRows( 80 | List> sources, 81 | int numRows, 82 | Counter counter, 83 | double staticFraction, 84 | int nSources, 85 | ReactiveFramework framework, 86 | ) { 87 | var prevRow = sources; 88 | final random = Random(0); 89 | final rows = >>[]; 90 | 91 | for (int l = 0; l < numRows; l++) { 92 | final row = _makeRow( 93 | prevRow, 94 | counter, 95 | staticFraction, 96 | nSources, 97 | framework, 98 | l, 99 | random, 100 | ); 101 | rows.add(row); 102 | prevRow = row; 103 | } 104 | 105 | return rows; 106 | } 107 | 108 | List> _makeRow( 109 | List> sources, 110 | Counter counter, 111 | double staticFraction, 112 | int nSources, 113 | ReactiveFramework framework, 114 | int layer, 115 | Random random, 116 | ) { 117 | return List.generate(sources.length, (myDex) { 118 | final mySources = List>.generate( 119 | nSources, 120 | (i) => sources[(myDex + i) % sources.length], 121 | ); 122 | 123 | final staticNode = random.nextDouble() < staticFraction; 124 | if (staticNode) { 125 | return framework.computed(() { 126 | counter.count++; 127 | var sum = 0; 128 | for (final src in mySources) { 129 | sum += src.read(); 130 | } 131 | return sum; 132 | }); 133 | } else { 134 | final first = mySources[0]; 135 | final tail = mySources.sublist(1); 136 | return framework.computed(() { 137 | counter.count++; 138 | var sum = first.read(); 139 | final shouldDrop = sum & 0x1; 140 | final dropDex = sum % tail.length; 141 | 142 | for (int i = 0; i < tail.length; i++) { 143 | if (shouldDrop != 0 && i == dropDex) continue; 144 | sum += tail[i].read(); 145 | } 146 | return sum; 147 | }); 148 | } 149 | }); 150 | } 151 | -------------------------------------------------------------------------------- /lib/utils/perf.dart: -------------------------------------------------------------------------------- 1 | class TimedResult { 2 | const TimedResult(this.result, this.time); 3 | 4 | final T result; 5 | final int time; 6 | } 7 | 8 | final _stopwatch = Stopwatch(); 9 | 10 | TimedResult runTimed(T Function() fn) { 11 | _stopwatch 12 | ..reset() 13 | ..start(); 14 | final result = fn(); 15 | _stopwatch.stop(); 16 | 17 | return TimedResult(result, _stopwatch.elapsedMicroseconds); 18 | } 19 | -------------------------------------------------------------------------------- /lib/utils/perf_logging.dart: -------------------------------------------------------------------------------- 1 | import '../framework_type.dart'; 2 | import 'perf_tests.dart'; 3 | 4 | class PerfRowStrings { 5 | const PerfRowStrings({ 6 | required this.framework, 7 | required this.test, 8 | required this.time, 9 | }); 10 | 11 | final String framework; 12 | final String test; 13 | final String time; 14 | } 15 | 16 | String _percent(num n) { 17 | return '${(n * 100).toStringAsFixed(1)}%'; 18 | } 19 | 20 | PerfRowStrings _trimColumns(PerfRowStrings row) { 21 | return PerfRowStrings( 22 | framework: row.framework.trim(), 23 | test: row.test.trim(), 24 | time: row.time.trim(), 25 | ); 26 | } 27 | 28 | void logPerfResult(PerfRowStrings row) { 29 | final trimmed = _trimColumns(row); 30 | print('| ${trimmed.framework} | ${trimmed.test} | ${trimmed.time} |'); 31 | } 32 | 33 | void printPerfReportHeaders() { 34 | logPerfResult(PerfRowStrings( 35 | framework: 'Framework', 36 | test: 'Test Case', 37 | time: 'Time (μs)', 38 | )); 39 | logPerfResult(PerfRowStrings( 40 | framework: '---', 41 | test: '---', 42 | time: '---', 43 | )); 44 | } 45 | 46 | PerfRowStrings perfRowStrings( 47 | String framework, 48 | TestConfig config, 49 | TimingResult timingResult, 50 | ) { 51 | final TestConfig(:expected) = config; 52 | final TimingResult(:result, :timing) = timingResult; 53 | 54 | final sum = expected.sum != 0 && expected.sum != result.sum 55 | ? 'sum: fial' 56 | : 'sum: pass'; 57 | final count = expected.count != 0 && expected.count != result.count 58 | ? 'count: fail' 59 | : 'count: pass'; 60 | 61 | return PerfRowStrings( 62 | framework: framework, 63 | test: '${makeTitle(config)} (${config.name ?? ''}, $sum, $count)', 64 | time: timing.time.toString(), 65 | ); 66 | } 67 | 68 | String makeTitle(TestConfig config) { 69 | final TestConfig( 70 | :width, 71 | :totalLayers, 72 | :staticFraction, 73 | :nSources, 74 | :readFraction 75 | ) = config; 76 | final dyn = staticFraction < 1 ? ' - dynamic' : ''; 77 | final read = readFraction < 1 ? ' - read ${_percent(readFraction)}' : ''; 78 | final sources = ' - $nSources sources'; 79 | 80 | return '${width}x$totalLayers$sources$dyn$read'; 81 | } 82 | -------------------------------------------------------------------------------- /lib/utils/perf_tests.dart: -------------------------------------------------------------------------------- 1 | class TestResult { 2 | const TestResult({ 3 | required this.sum, 4 | required this.count, 5 | }); 6 | 7 | final num sum; 8 | final int count; 9 | } 10 | 11 | class TestTiming { 12 | const TestTiming(this.time); 13 | final int time; 14 | } 15 | 16 | class TimingResult { 17 | const TimingResult(this.result, this.timing); 18 | 19 | final T result; 20 | final TestTiming timing; 21 | } 22 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | lints: 5 | dependency: "direct dev" 6 | description: 7 | name: lints 8 | sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "5.1.1" 12 | sdks: 13 | dart: ">=3.6.0 <4.0.0" 14 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: reactivity_benchmark 2 | description: Benchmark comparing different standalone Dart reactivity/signals frameworks. 3 | repository: https://github.com/medz/dart-reactivity-benchmark 4 | funding: 5 | - https://github.com/sponsors/medz 6 | - https://opencollective.com/openodroe 7 | 8 | version: 0.0.1 9 | environment: 10 | sdk: ^3.6.0 11 | 12 | dev_dependencies: 13 | lints: ^5.1.1 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | --------------------------------------------------------------------------------