commit 27c05c982f498043b0a55ab9f80526996e930285 Author: Valère Plantevin Date: Mon Apr 20 21:23:37 2026 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c0adf1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,182 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,jetbrains+all,macos,linux,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,jetbrains+all,macos,linux,windows + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,jetbrains+all,macos,linux,windows \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9ea30b3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ecs_dt_benchmark" +version = "0.1.0" +edition = "2021" +description = "ECS Digital Twin runtime benchmark — TRANSIT lab / UQAC" + +# No default features: we do NOT want a window, renderer, audio, or anything +# that requires a display. Pure headless ECS. +[dependencies] +bevy_ecs = { version = "0.18", default-features = false } +rand = { version = "0.8", features = ["small_rng"] } +rand_chacha = "0.3" + +[profile.release] +opt-level = 3 +lto = "thin" # thin LTO: good balance between link time and perf +codegen-units = 1 # single CGU for max inlining across crate boundary +panic = "abort" # smaller binary, no unwinding overhead + +[profile.bench] +inherits = "release" +debug = true # keep symbols so perf/flamegraph works on RPi + +# On aarch64 (RPi 5 Cortex-A76) the compiler can use NEON by default. +# Uncomment the line below for the RPi 5 build to also enable SVE if your +# toolchain supports it (most don't yet for aarch64-unknown-linux-gnu): +# [target.aarch64-unknown-linux-gnu] +# rustflags = ["-C", "target-cpu=cortex-a76"] \ No newline at end of file diff --git a/results/csv/entities_10000.txt b/results/csv/entities_10000.txt new file mode 100644 index 0000000..53e13b1 --- /dev/null +++ b/results/csv/entities_10000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 94.0 sim= 20.0 export= 11.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 6.0 export= 3.9 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.2 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.8 µs | ingest= 48.9 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 0.531 s +Sustained tick rate: 9424.5 Hz +Per-tick mean (wall): 10.6 µs + +Per-system mean (µs): +IngestSystem: 48.8 +SimulationSystem: 5.9 +ExportSystem: 3.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_100000.txt b/results/csv/entities_100000.txt new file mode 100644 index 0000000..b94736a --- /dev/null +++ b/results/csv/entities_100000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 202.0 sim= 75.0 export= 41.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 0.0 MB | tick_mean= 1101.8 µs | ingest= 500.9 sim= 61.2 export= 40.6 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 0.0 MB | tick_mean= 1105.1 µs | ingest= 501.0 sim= 61.1 export= 40.6 diag= 0.0 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 0.0 MB | tick_mean= 1114.2 µs | ingest= 504.7 sim= 61.5 export= 40.8 diag= 0.0 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 226, act=10000) | faults=3624295 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.0 µs | ingest= 505.0 sim= 61.7 export= 40.9 diag= 0.0 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 206, act=10000) | faults=4525217 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.9 µs | ingest= 504.7 sim= 61.8 export= 40.9 diag= 0.0 µs | exported=3655.68 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 5.555 s +Sustained tick rate: 900.2 Hz +Per-tick mean (wall): 111.2 µs + +Per-system mean (µs): +IngestSystem: 504.2 +SimulationSystem: 61.8 +ExportSystem: 41.0 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.96 MB +Acks processed: 50000000 +Alerts seen: 1028407 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_150000.txt b/results/csv/entities_150000.txt new file mode 100644 index 0000000..7817a38 --- /dev/null +++ b/results/csv/entities_150000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 306.0 sim= 113.0 export= 61.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 326, act=15000) | faults=1385323 lockouts= 0 | rss= 0.0 MB | tick_mean= 1717.3 µs | ingest= 755.7 sim= 92.9 export= 61.8 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 278, act=15000) | faults=2736064 lockouts= 0 | rss= 0.0 MB | tick_mean= 1721.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=1828.59 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 330, act=15000) | faults=4087020 lockouts= 0 | rss= 0.0 MB | tick_mean= 1719.6 µs | ingest= 753.9 sim= 93.0 export= 61.5 diag= 0.0 µs | exported=3046.89 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 323, act=15000) | faults=5437256 lockouts= 0 | rss= 0.0 MB | tick_mean= 1715.2 µs | ingest= 753.4 sim= 92.6 export= 61.5 diag= 0.0 µs | exported=4265.19 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 334, act=15000) | faults=6786973 lockouts= 0 | rss= 0.0 MB | tick_mean= 1726.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=5483.51 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 8.604 s +Sustained tick rate: 581.1 Hz +Per-tick mean (wall): 171.6 µs + +Per-system mean (µs): +IngestSystem: 754.2 +SimulationSystem: 92.5 +ExportSystem: 61.7 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.45 MB +Acks processed: 75000000 +Alerts seen: 1543036 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_200000.txt b/results/csv/entities_200000.txt new file mode 100644 index 0000000..78ff5ca --- /dev/null +++ b/results/csv/entities_200000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 415.0 sim= 160.0 export= 84.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 390, act=20000) | faults=1847083 lockouts= 0 | rss= 0.0 MB | tick_mean= 2413.0 µs | ingest=1003.2 sim= 125.4 export= 83.3 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 385, act=20000) | faults=3647521 lockouts= 0 | rss= 0.0 MB | tick_mean= 2409.9 µs | ingest=1003.2 sim= 125.4 export= 83.1 diag= 0.0 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 391, act=20000) | faults=5447700 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.4 µs | ingest= 998.3 sim= 124.5 export= 82.8 diag= 0.0 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 451, act=20000) | faults=7247668 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.1 µs | ingest= 998.7 sim= 124.7 export= 82.5 diag= 0.0 µs | exported=5686.95 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 424, act=20000) | faults=9047286 lockouts= 0 | rss= 0.0 MB | tick_mean= 2386.7 µs | ingest= 999.0 sim= 124.7 export= 82.8 diag= 0.0 µs | exported=7311.35 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 11.966 s +Sustained tick rate: 417.9 Hz +Per-tick mean (wall): 239.4 µs + +Per-system mean (µs): +IngestSystem: 1001.3 +SimulationSystem: 125.2 +ExportSystem: 83.1 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.92 MB +Acks processed: 100000000 +Alerts seen: 2056796 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_25000.txt b/results/csv/entities_25000.txt new file mode 100644 index 0000000..b4ae793 --- /dev/null +++ b/results/csv/entities_25000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 67.0 sim= 19.0 export= 11.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.4 µs | ingest= 125.7 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.6 µs | ingest= 125.8 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 0.0 MB | tick_mean= 276.1 µs | ingest= 126.4 sim= 15.1 export= 10.0 diag= 0.0 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.5 µs | ingest= 125.7 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.9 µs | ingest= 125.9 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.378 s +Sustained tick rate: 3629.5 Hz +Per-tick mean (wall): 27.5 µs + +Per-system mean (µs): +IngestSystem: 125.6 +SimulationSystem: 15.0 +ExportSystem: 9.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_50000.txt b/results/csv/entities_50000.txt new file mode 100644 index 0000000..83135a4 --- /dev/null +++ b/results/csv/entities_50000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 104.0 sim= 34.0 export= 18.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 0.0 MB | tick_mean= 548.8 µs | ingest= 254.6 sim= 30.7 export= 20.0 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 0.0 MB | tick_mean= 546.7 µs | ingest= 253.0 sim= 30.3 export= 19.9 diag= 0.0 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.3 µs | ingest= 254.3 sim= 30.6 export= 20.1 diag= 0.0 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.2 µs | ingest= 254.1 sim= 30.5 export= 20.0 diag= 0.0 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 0.0 MB | tick_mean= 545.8 µs | ingest= 252.6 sim= 30.4 export= 19.9 diag= 0.0 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 2.743 s +Sustained tick rate: 1822.8 Hz +Per-tick mean (wall): 54.9 µs + +Per-system mean (µs): +IngestSystem: 254.0 +SimulationSystem: 30.5 +ExportSystem: 19.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/csv/entities_75000.txt b/results/csv/entities_75000.txt new file mode 100644 index 0000000..75b5637 --- /dev/null +++ b/results/csv/entities_75000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 188.0 sim= 62.0 export= 32.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 133, act= 7500) | faults=693165 lockouts= 0 | rss= 0.0 MB | tick_mean= 818.6 µs | ingest= 378.8 sim= 45.8 export= 30.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 163, act= 7500) | faults=1367793 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.4 µs | ingest= 377.4 sim= 45.7 export= 30.6 diag= 0.0 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 145, act= 7500) | faults=2042643 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.6 µs | ingest= 377.7 sim= 45.8 export= 30.6 diag= 0.0 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 169, act= 7500) | faults=2717674 lockouts= 0 | rss= 0.0 MB | tick_mean= 815.9 µs | ingest= 376.7 sim= 45.6 export= 30.5 diag= 0.0 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 162, act= 7500) | faults=3393495 lockouts= 0 | rss= 0.0 MB | tick_mean= 820.1 µs | ingest= 377.7 sim= 45.7 export= 30.7 diag= 0.0 µs | exported=2741.78 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.094 s +Sustained tick rate: 1221.3 Hz +Per-tick mean (wall): 82.0 µs + +Per-system mean (µs): +IngestSystem: 377.6 +SimulationSystem: 45.7 +ExportSystem: 30.6 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.72 MB +Acks processed: 37500000 +Alerts seen: 771425 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/final_table.csv b/results/final_table.csv new file mode 100644 index 0000000..81478e2 --- /dev/null +++ b/results/final_table.csv @@ -0,0 +1,8 @@ +entities,ticks,wall_s,hz,per_tick_us,ingest_us,sim_us,export_us,diag_us,sensor_faults,actuator_lockouts,exported_mb,acks,alerts,rss_mb +10000,5000,,,,48.8,5.9,3.9,0.0,498016,0,,5000000,104943, +25000,5000,,,,125.6,15.0,9.9,0.0,1243639,0,,12500000,254654, +50000,5000,,,,254.0,30.5,19.9,0.0,2486121,0,,25000000,511654, +75000,5000,,,,377.6,45.7,30.6,0.0,3730093,0,,37500000,771425, +100000,5000,,,,504.2,61.8,41.0,0.0,4974449,0,,50000000,1028407, +150000,5000,,,,754.2,92.5,61.7,0.0,7460636,0,,75000000,1543036, +200000,5000,,,,1001.3,125.2,83.1,0.0,9946420,0,,100000000,2056796, diff --git a/results/raw_10000.txt b/results/raw_10000.txt new file mode 100644 index 0000000..53e13b1 --- /dev/null +++ b/results/raw_10000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 94.0 sim= 20.0 export= 11.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 6.0 export= 3.9 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.2 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.8 µs | ingest= 48.9 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 0.531 s +Sustained tick rate: 9424.5 Hz +Per-tick mean (wall): 10.6 µs + +Per-system mean (µs): +IngestSystem: 48.8 +SimulationSystem: 5.9 +ExportSystem: 3.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_100000.txt b/results/raw_100000.txt new file mode 100644 index 0000000..b94736a --- /dev/null +++ b/results/raw_100000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 202.0 sim= 75.0 export= 41.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 0.0 MB | tick_mean= 1101.8 µs | ingest= 500.9 sim= 61.2 export= 40.6 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 0.0 MB | tick_mean= 1105.1 µs | ingest= 501.0 sim= 61.1 export= 40.6 diag= 0.0 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 0.0 MB | tick_mean= 1114.2 µs | ingest= 504.7 sim= 61.5 export= 40.8 diag= 0.0 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 226, act=10000) | faults=3624295 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.0 µs | ingest= 505.0 sim= 61.7 export= 40.9 diag= 0.0 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 206, act=10000) | faults=4525217 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.9 µs | ingest= 504.7 sim= 61.8 export= 40.9 diag= 0.0 µs | exported=3655.68 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 5.555 s +Sustained tick rate: 900.2 Hz +Per-tick mean (wall): 111.2 µs + +Per-system mean (µs): +IngestSystem: 504.2 +SimulationSystem: 61.8 +ExportSystem: 41.0 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.96 MB +Acks processed: 50000000 +Alerts seen: 1028407 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_150000.txt b/results/raw_150000.txt new file mode 100644 index 0000000..7817a38 --- /dev/null +++ b/results/raw_150000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 306.0 sim= 113.0 export= 61.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 326, act=15000) | faults=1385323 lockouts= 0 | rss= 0.0 MB | tick_mean= 1717.3 µs | ingest= 755.7 sim= 92.9 export= 61.8 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 278, act=15000) | faults=2736064 lockouts= 0 | rss= 0.0 MB | tick_mean= 1721.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=1828.59 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 330, act=15000) | faults=4087020 lockouts= 0 | rss= 0.0 MB | tick_mean= 1719.6 µs | ingest= 753.9 sim= 93.0 export= 61.5 diag= 0.0 µs | exported=3046.89 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 323, act=15000) | faults=5437256 lockouts= 0 | rss= 0.0 MB | tick_mean= 1715.2 µs | ingest= 753.4 sim= 92.6 export= 61.5 diag= 0.0 µs | exported=4265.19 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 334, act=15000) | faults=6786973 lockouts= 0 | rss= 0.0 MB | tick_mean= 1726.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=5483.51 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 8.604 s +Sustained tick rate: 581.1 Hz +Per-tick mean (wall): 171.6 µs + +Per-system mean (µs): +IngestSystem: 754.2 +SimulationSystem: 92.5 +ExportSystem: 61.7 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.45 MB +Acks processed: 75000000 +Alerts seen: 1543036 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_200000.txt b/results/raw_200000.txt new file mode 100644 index 0000000..78ff5ca --- /dev/null +++ b/results/raw_200000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 415.0 sim= 160.0 export= 84.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 390, act=20000) | faults=1847083 lockouts= 0 | rss= 0.0 MB | tick_mean= 2413.0 µs | ingest=1003.2 sim= 125.4 export= 83.3 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 385, act=20000) | faults=3647521 lockouts= 0 | rss= 0.0 MB | tick_mean= 2409.9 µs | ingest=1003.2 sim= 125.4 export= 83.1 diag= 0.0 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 391, act=20000) | faults=5447700 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.4 µs | ingest= 998.3 sim= 124.5 export= 82.8 diag= 0.0 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 451, act=20000) | faults=7247668 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.1 µs | ingest= 998.7 sim= 124.7 export= 82.5 diag= 0.0 µs | exported=5686.95 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 424, act=20000) | faults=9047286 lockouts= 0 | rss= 0.0 MB | tick_mean= 2386.7 µs | ingest= 999.0 sim= 124.7 export= 82.8 diag= 0.0 µs | exported=7311.35 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 11.966 s +Sustained tick rate: 417.9 Hz +Per-tick mean (wall): 239.4 µs + +Per-system mean (µs): +IngestSystem: 1001.3 +SimulationSystem: 125.2 +ExportSystem: 83.1 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.92 MB +Acks processed: 100000000 +Alerts seen: 2056796 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_25000.txt b/results/raw_25000.txt new file mode 100644 index 0000000..b4ae793 --- /dev/null +++ b/results/raw_25000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 67.0 sim= 19.0 export= 11.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.4 µs | ingest= 125.7 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.6 µs | ingest= 125.8 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 0.0 MB | tick_mean= 276.1 µs | ingest= 126.4 sim= 15.1 export= 10.0 diag= 0.0 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.5 µs | ingest= 125.7 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.9 µs | ingest= 125.9 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.378 s +Sustained tick rate: 3629.5 Hz +Per-tick mean (wall): 27.5 µs + +Per-system mean (µs): +IngestSystem: 125.6 +SimulationSystem: 15.0 +ExportSystem: 9.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_50000.txt b/results/raw_50000.txt new file mode 100644 index 0000000..83135a4 --- /dev/null +++ b/results/raw_50000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 104.0 sim= 34.0 export= 18.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 0.0 MB | tick_mean= 548.8 µs | ingest= 254.6 sim= 30.7 export= 20.0 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 0.0 MB | tick_mean= 546.7 µs | ingest= 253.0 sim= 30.3 export= 19.9 diag= 0.0 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.3 µs | ingest= 254.3 sim= 30.6 export= 20.1 diag= 0.0 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.2 µs | ingest= 254.1 sim= 30.5 export= 20.0 diag= 0.0 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 0.0 MB | tick_mean= 545.8 µs | ingest= 252.6 sim= 30.4 export= 19.9 diag= 0.0 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 2.743 s +Sustained tick rate: 1822.8 Hz +Per-tick mean (wall): 54.9 µs + +Per-system mean (µs): +IngestSystem: 254.0 +SimulationSystem: 30.5 +ExportSystem: 19.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/raw_75000.txt b/results/raw_75000.txt new file mode 100644 index 0000000..75b5637 --- /dev/null +++ b/results/raw_75000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 188.0 sim= 62.0 export= 32.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 133, act= 7500) | faults=693165 lockouts= 0 | rss= 0.0 MB | tick_mean= 818.6 µs | ingest= 378.8 sim= 45.8 export= 30.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 163, act= 7500) | faults=1367793 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.4 µs | ingest= 377.4 sim= 45.7 export= 30.6 diag= 0.0 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 145, act= 7500) | faults=2042643 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.6 µs | ingest= 377.7 sim= 45.8 export= 30.6 diag= 0.0 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 169, act= 7500) | faults=2717674 lockouts= 0 | rss= 0.0 MB | tick_mean= 815.9 µs | ingest= 376.7 sim= 45.6 export= 30.5 diag= 0.0 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 162, act= 7500) | faults=3393495 lockouts= 0 | rss= 0.0 MB | tick_mean= 820.1 µs | ingest= 377.7 sim= 45.7 export= 30.7 diag= 0.0 µs | exported=2741.78 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.094 s +Sustained tick rate: 1221.3 Hz +Per-tick mean (wall): 82.0 µs + +Per-system mean (µs): +IngestSystem: 377.6 +SimulationSystem: 45.7 +ExportSystem: 30.6 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.72 MB +Acks processed: 37500000 +Alerts seen: 771425 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/summary.txt b/results/summary.txt new file mode 100644 index 0000000..5e6895c --- /dev/null +++ b/results/summary.txt @@ -0,0 +1,271 @@ +ECS DT Benchmark Sweep — Mon Apr 20 21:17:09 UTC 2026 +=== System Information === +Date: 2026-04-20 21:17:09 UTC +OS: Darwin 25.4.0 +Arch: arm64 +Target CPU: apple-m1 +Rust: rustc 1.94.0 (4a4ef493e 2026-03-02) +Cargo: cargo 1.94.0 (85eff7c80 2026-01-15) + +Apple M4 Max +Memory: 128.0 GB + + +=== entities=10000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 94.0 sim= 20.0 export= 11.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 6.0 export= 3.9 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.2 µs | ingest= 48.6 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.6 µs | ingest= 48.8 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 0.0 MB | tick_mean= 105.8 µs | ingest= 48.9 sim= 5.9 export= 3.9 diag= 0.0 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 0.531 s +Sustained tick rate: 9424.5 Hz +Per-tick mean (wall): 10.6 µs + +Per-system mean (µs): +IngestSystem: 48.8 +SimulationSystem: 5.9 +ExportSystem: 3.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=25000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 67.0 sim= 19.0 export= 11.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.4 µs | ingest= 125.7 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.6 µs | ingest= 125.8 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 0.0 MB | tick_mean= 276.1 µs | ingest= 126.4 sim= 15.1 export= 10.0 diag= 0.0 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.5 µs | ingest= 125.7 sim= 15.1 export= 9.9 diag= 0.0 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 0.0 MB | tick_mean= 274.9 µs | ingest= 125.9 sim= 15.0 export= 9.9 diag= 0.0 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.378 s +Sustained tick rate: 3629.5 Hz +Per-tick mean (wall): 27.5 µs + +Per-system mean (µs): +IngestSystem: 125.6 +SimulationSystem: 15.0 +ExportSystem: 9.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=50000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 104.0 sim= 34.0 export= 18.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 0.0 MB | tick_mean= 548.8 µs | ingest= 254.6 sim= 30.7 export= 20.0 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 0.0 MB | tick_mean= 546.7 µs | ingest= 253.0 sim= 30.3 export= 19.9 diag= 0.0 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.3 µs | ingest= 254.3 sim= 30.6 export= 20.1 diag= 0.0 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 0.0 MB | tick_mean= 549.2 µs | ingest= 254.1 sim= 30.5 export= 20.0 diag= 0.0 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 0.0 MB | tick_mean= 545.8 µs | ingest= 252.6 sim= 30.4 export= 19.9 diag= 0.0 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 2.743 s +Sustained tick rate: 1822.8 Hz +Per-tick mean (wall): 54.9 µs + +Per-system mean (µs): +IngestSystem: 254.0 +SimulationSystem: 30.5 +ExportSystem: 19.9 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=75000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 188.0 sim= 62.0 export= 32.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 133, act= 7500) | faults=693165 lockouts= 0 | rss= 0.0 MB | tick_mean= 818.6 µs | ingest= 378.8 sim= 45.8 export= 30.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 163, act= 7500) | faults=1367793 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.4 µs | ingest= 377.4 sim= 45.7 export= 30.6 diag= 0.0 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 145, act= 7500) | faults=2042643 lockouts= 0 | rss= 0.0 MB | tick_mean= 817.6 µs | ingest= 377.7 sim= 45.8 export= 30.6 diag= 0.0 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 169, act= 7500) | faults=2717674 lockouts= 0 | rss= 0.0 MB | tick_mean= 815.9 µs | ingest= 376.7 sim= 45.6 export= 30.5 diag= 0.0 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 162, act= 7500) | faults=3393495 lockouts= 0 | rss= 0.0 MB | tick_mean= 820.1 µs | ingest= 377.7 sim= 45.7 export= 30.7 diag= 0.0 µs | exported=2741.78 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.094 s +Sustained tick rate: 1221.3 Hz +Per-tick mean (wall): 82.0 µs + +Per-system mean (µs): +IngestSystem: 377.6 +SimulationSystem: 45.7 +ExportSystem: 30.6 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.72 MB +Acks processed: 37500000 +Alerts seen: 771425 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=100000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 202.0 sim= 75.0 export= 41.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 0.0 MB | tick_mean= 1101.8 µs | ingest= 500.9 sim= 61.2 export= 40.6 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 0.0 MB | tick_mean= 1105.1 µs | ingest= 501.0 sim= 61.1 export= 40.6 diag= 0.0 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 0.0 MB | tick_mean= 1114.2 µs | ingest= 504.7 sim= 61.5 export= 40.8 diag= 0.0 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 226, act=10000) | faults=3624295 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.0 µs | ingest= 505.0 sim= 61.7 export= 40.9 diag= 0.0 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 206, act=10000) | faults=4525217 lockouts= 0 | rss= 0.0 MB | tick_mean= 1112.9 µs | ingest= 504.7 sim= 61.8 export= 40.9 diag= 0.0 µs | exported=3655.68 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 5.555 s +Sustained tick rate: 900.2 Hz +Per-tick mean (wall): 111.2 µs + +Per-system mean (µs): +IngestSystem: 504.2 +SimulationSystem: 61.8 +ExportSystem: 41.0 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.96 MB +Acks processed: 50000000 +Alerts seen: 1028407 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=150000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 306.0 sim= 113.0 export= 61.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 326, act=15000) | faults=1385323 lockouts= 0 | rss= 0.0 MB | tick_mean= 1717.3 µs | ingest= 755.7 sim= 92.9 export= 61.8 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 278, act=15000) | faults=2736064 lockouts= 0 | rss= 0.0 MB | tick_mean= 1721.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=1828.59 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 330, act=15000) | faults=4087020 lockouts= 0 | rss= 0.0 MB | tick_mean= 1719.6 µs | ingest= 753.9 sim= 93.0 export= 61.5 diag= 0.0 µs | exported=3046.89 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 323, act=15000) | faults=5437256 lockouts= 0 | rss= 0.0 MB | tick_mean= 1715.2 µs | ingest= 753.4 sim= 92.6 export= 61.5 diag= 0.0 µs | exported=4265.19 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 334, act=15000) | faults=6786973 lockouts= 0 | rss= 0.0 MB | tick_mean= 1726.7 µs | ingest= 755.2 sim= 93.0 export= 61.8 diag= 0.0 µs | exported=5483.51 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 8.604 s +Sustained tick rate: 581.1 Hz +Per-tick mean (wall): 171.6 µs + +Per-system mean (µs): +IngestSystem: 754.2 +SimulationSystem: 92.5 +ExportSystem: 61.7 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.45 MB +Acks processed: 75000000 +Alerts seen: 1543036 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── + +=== entities=200000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 0.0 MB | tick_mean= 0.0 µs | ingest= 415.0 sim= 160.0 export= 84.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 390, act=20000) | faults=1847083 lockouts= 0 | rss= 0.0 MB | tick_mean= 2413.0 µs | ingest=1003.2 sim= 125.4 export= 83.3 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 385, act=20000) | faults=3647521 lockouts= 0 | rss= 0.0 MB | tick_mean= 2409.9 µs | ingest=1003.2 sim= 125.4 export= 83.1 diag= 0.0 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 391, act=20000) | faults=5447700 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.4 µs | ingest= 998.3 sim= 124.5 export= 82.8 diag= 0.0 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 451, act=20000) | faults=7247668 lockouts= 0 | rss= 0.0 MB | tick_mean= 2380.1 µs | ingest= 998.7 sim= 124.7 export= 82.5 diag= 0.0 µs | exported=5686.95 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 424, act=20000) | faults=9047286 lockouts= 0 | rss= 0.0 MB | tick_mean= 2386.7 µs | ingest= 999.0 sim= 124.7 export= 82.8 diag= 0.0 µs | exported=7311.35 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 11.966 s +Sustained tick rate: 417.9 Hz +Per-tick mean (wall): 239.4 µs + +Per-system mean (µs): +IngestSystem: 1001.3 +SimulationSystem: 125.2 +ExportSystem: 83.1 +DiagnosticsSystem: 0.0 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.92 MB +Acks processed: 100000000 +Alerts seen: 2056796 + +Memory (RSS at end): 0.0 MB +──────────────────────────────────────────────────────── diff --git a/results/sysinfo.txt b/results/sysinfo.txt new file mode 100644 index 0000000..d764cab --- /dev/null +++ b/results/sysinfo.txt @@ -0,0 +1,10 @@ +=== System Information === +Date: 2026-04-20 21:17:09 UTC +OS: Darwin 25.4.0 +Arch: arm64 +Target CPU: apple-m1 +Rust: rustc 1.94.0 (4a4ef493e 2026-03-02) +Cargo: cargo 1.94.0 (85eff7c80 2026-01-15) + +Apple M4 Max +Memory: 128.0 GB diff --git a/results_rpi/csv/entities_10000.txt b/results_rpi/csv/entities_10000.txt new file mode 100644 index 0000000..835e949 --- /dev/null +++ b/results_rpi/csv/entities_10000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 4.0 MB | tick_mean= 0.0 µs | ingest= 97.0 sim= 27.0 export= 14.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 6.3 MB | tick_mean= 284.8 µs | ingest= 97.1 sim= 17.1 export= 14.0 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 7.8 MB | tick_mean= 286.0 µs | ingest= 97.1 sim= 17.2 export= 14.1 diag= 0.1 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 9.3 MB | tick_mean= 286.1 µs | ingest= 97.3 sim= 17.3 export= 14.1 diag= 0.1 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 10.9 MB | tick_mean= 284.8 µs | ingest= 97.0 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 12.4 MB | tick_mean= 284.6 µs | ingest= 97.1 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.430 s +Sustained tick rate: 3497.7 Hz +Per-tick mean (wall): 28.5 µs + +Per-system mean (µs): +IngestSystem: 97.2 +SimulationSystem: 17.2 +ExportSystem: 14.0 +DiagnosticsSystem: 0.1 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 13.1 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_100000.txt b/results_rpi/csv/entities_100000.txt new file mode 100644 index 0000000..15ea4f6 --- /dev/null +++ b/results_rpi/csv/entities_100000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 16.3 MB | tick_mean= 0.0 µs | ingest= 948.0 sim= 607.0 export= 157.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 36.7 MB | tick_mean= 4126.0 µs | ingest= 986.9 sim= 218.2 export= 150.2 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 51.9 MB | tick_mean= 4197.8 µs | ingest= 987.7 sim= 236.6 export= 152.8 diag= 0.1 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 67.2 MB | tick_mean= 4132.4 µs | ingest= 986.1 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 209, act=10000) | faults=3624295 lockouts= 0 | rss= 82.4 MB | tick_mean= 4132.1 µs | ingest= 986.5 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 187, act=10000) | faults=4525217 lockouts= 0 | rss= 97.7 MB | tick_mean= 4132.9 µs | ingest= 985.3 sim= 217.1 export= 150.0 diag= 0.1 µs | exported=3655.69 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 20.732 s +Sustained tick rate: 241.2 Hz +Per-tick mean (wall): 413.0 µs + +Per-system mean (µs): +IngestSystem: 985.7 +SimulationSystem: 217.2 +ExportSystem: 150.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.97 MB +Acks processed: 50000000 +Alerts seen: 1029861 + +Memory (RSS at end): 105.3 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_150000.txt b/results_rpi/csv/entities_150000.txt new file mode 100644 index 0000000..35d7438 --- /dev/null +++ b/results_rpi/csv/entities_150000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 24.5 MB | tick_mean= 0.0 µs | ingest=1444.0 sim= 884.0 export= 251.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 306, act=15000) | faults=1385323 lockouts= 0 | rss= 54.3 MB | tick_mean= 6453.6 µs | ingest=1517.1 sim= 381.6 export= 226.7 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 279, act=15000) | faults=2736064 lockouts= 0 | rss= 77.2 MB | tick_mean= 6469.3 µs | ingest=1517.2 sim= 381.6 export= 226.8 diag= 0.1 µs | exported=1828.57 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 317, act=15000) | faults=4087020 lockouts= 0 | rss= 100.1 MB | tick_mean= 6473.9 µs | ingest=1519.6 sim= 382.4 export= 227.0 diag= 0.1 µs | exported=3046.87 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 320, act=15000) | faults=5437256 lockouts= 0 | rss= 123.0 MB | tick_mean= 6470.4 µs | ingest=1518.3 sim= 381.8 export= 226.7 diag= 0.1 µs | exported=4265.17 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 323, act=15000) | faults=6786973 lockouts= 0 | rss= 145.8 MB | tick_mean= 6470.2 µs | ingest=1518.6 sim= 382.0 export= 226.7 diag= 0.1 µs | exported=5483.48 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 32.355 s +Sustained tick rate: 154.5 Hz +Per-tick mean (wall): 646.7 µs + +Per-system mean (µs): +IngestSystem: 1517.3 +SimulationSystem: 381.4 +ExportSystem: 226.6 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.41 MB +Acks processed: 75000000 +Alerts seen: 1538845 + +Memory (RSS at end): 157.2 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_200000.txt b/results_rpi/csv/entities_200000.txt new file mode 100644 index 0000000..82d8c1f --- /dev/null +++ b/results_rpi/csv/entities_200000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 30.0 MB | tick_mean= 0.0 µs | ingest=1907.0 sim=1336.0 export= 349.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 405, act=20000) | faults=1847083 lockouts= 0 | rss= 69.5 MB | tick_mean= 8788.2 µs | ingest=2023.3 sim= 582.9 export= 300.9 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 396, act=20000) | faults=3647521 lockouts= 0 | rss= 100.0 MB | tick_mean= 8805.3 µs | ingest=2023.3 sim= 582.9 export= 300.5 diag= 0.1 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 408, act=20000) | faults=5447700 lockouts= 0 | rss= 130.5 MB | tick_mean= 8805.5 µs | ingest=2023.7 sim= 582.7 export= 300.4 diag= 0.1 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 444, act=20000) | faults=7247668 lockouts= 0 | rss= 161.1 MB | tick_mean= 8808.2 µs | ingest=2024.8 sim= 583.4 export= 300.7 diag= 0.2 µs | exported=5686.94 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 429, act=20000) | faults=9047286 lockouts= 0 | rss= 191.6 MB | tick_mean= 8804.6 µs | ingest=2022.3 sim= 582.4 export= 300.7 diag= 0.1 µs | exported=7311.36 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 44.034 s +Sustained tick rate: 113.5 Hz +Per-tick mean (wall): 880.6 µs + +Per-system mean (µs): +IngestSystem: 2022.7 +SimulationSystem: 582.0 +ExportSystem: 300.7 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.94 MB +Acks processed: 100000000 +Alerts seen: 2059136 + +Memory (RSS at end): 206.8 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_25000.txt b/results_rpi/csv/entities_25000.txt new file mode 100644 index 0000000..faa38ce --- /dev/null +++ b/results_rpi/csv/entities_25000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 6.0 MB | tick_mean= 0.0 µs | ingest= 236.0 sim= 81.0 export= 36.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 11.6 MB | tick_mean= 812.9 µs | ingest= 243.3 sim= 43.2 export= 35.1 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 15.5 MB | tick_mean= 812.1 µs | ingest= 244.1 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 19.2 MB | tick_mean= 811.6 µs | ingest= 243.6 sim= 43.0 export= 35.1 diag= 0.1 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 23.1 MB | tick_mean= 814.2 µs | ingest= 244.0 sim= 43.3 export= 35.1 diag= 0.1 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 26.9 MB | tick_mean= 811.4 µs | ingest= 243.6 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.066 s +Sustained tick rate: 1229.7 Hz +Per-tick mean (wall): 81.3 µs + +Per-system mean (µs): +IngestSystem: 244.0 +SimulationSystem: 43.2 +ExportSystem: 35.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 28.8 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_50000.txt b/results_rpi/csv/entities_50000.txt new file mode 100644 index 0000000..e4e5682 --- /dev/null +++ b/results_rpi/csv/entities_50000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 9.4 MB | tick_mean= 0.0 µs | ingest= 528.0 sim= 211.0 export= 73.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 20.1 MB | tick_mean= 1918.9 µs | ingest= 497.0 sim= 94.6 export= 70.9 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 27.7 MB | tick_mean= 1922.8 µs | ingest= 496.6 sim= 94.4 export= 71.0 diag= 0.1 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 35.3 MB | tick_mean= 1924.0 µs | ingest= 497.0 sim= 94.7 export= 71.0 diag= 0.1 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 43.0 MB | tick_mean= 1922.6 µs | ingest= 496.6 sim= 94.5 export= 70.8 diag= 0.1 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 50.6 MB | tick_mean= 1921.7 µs | ingest= 496.7 sim= 94.5 export= 70.9 diag= 0.1 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 9.617 s +Sustained tick rate: 519.9 Hz +Per-tick mean (wall): 192.0 µs + +Per-system mean (µs): +IngestSystem: 496.0 +SimulationSystem: 94.5 +ExportSystem: 70.8 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 54.3 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/csv/entities_75000.txt b/results_rpi/csv/entities_75000.txt new file mode 100644 index 0000000..20b48aa --- /dev/null +++ b/results_rpi/csv/entities_75000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 13.6 MB | tick_mean= 0.0 µs | ingest= 713.0 sim= 383.0 export= 112.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 142, act= 7500) | faults=693165 lockouts= 0 | rss= 28.7 MB | tick_mean= 3016.1 µs | ingest= 735.8 sim= 156.6 export= 110.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 166, act= 7500) | faults=1367793 lockouts= 0 | rss= 40.1 MB | tick_mean= 3021.4 µs | ingest= 735.8 sim= 156.7 export= 110.8 diag= 0.1 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 159, act= 7500) | faults=2042643 lockouts= 0 | rss= 51.5 MB | tick_mean= 3024.8 µs | ingest= 736.4 sim= 156.9 export= 110.8 diag= 0.1 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 166, act= 7500) | faults=2717674 lockouts= 0 | rss= 63.0 MB | tick_mean= 3022.6 µs | ingest= 735.3 sim= 156.9 export= 110.8 diag= 0.2 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 155, act= 7500) | faults=3393495 lockouts= 0 | rss= 74.4 MB | tick_mean= 3021.8 µs | ingest= 735.2 sim= 156.5 export= 110.8 diag= 0.2 µs | exported=2741.79 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 15.118 s +Sustained tick rate: 330.7 Hz +Per-tick mean (wall): 302.3 µs + +Per-system mean (µs): +IngestSystem: 736.2 +SimulationSystem: 156.6 +ExportSystem: 110.7 +DiagnosticsSystem: 0.3 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.74 MB +Acks processed: 37500000 +Alerts seen: 773395 + +Memory (RSS at end): 80.1 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/final_table.csv b/results_rpi/final_table.csv new file mode 100644 index 0000000..02cfe1e --- /dev/null +++ b/results_rpi/final_table.csv @@ -0,0 +1,8 @@ +entities,ticks,wall_s,hz,per_tick_us,ingest_us,sim_us,export_us,diag_us,sensor_faults,actuator_lockouts,exported_mb,acks,alerts,rss_mb +10000,5000,,,,97.2,17.2,14.0,0.1,498016,0,,5000000,104943, +25000,5000,,,,244.0,43.2,35.1,0.2,1243639,0,,12500000,254654, +50000,5000,,,,496.0,94.5,70.8,0.2,2486121,0,,25000000,511654, +75000,5000,,,,736.2,156.6,110.7,0.3,3730093,0,,37500000,773395, +100000,5000,,,,985.7,217.2,150.1,0.2,4974449,0,,50000000,1029861, +150000,5000,,,,1517.3,381.4,226.6,0.2,7460636,0,,75000000,1538845, +200000,5000,,,,2022.7,582.0,300.7,0.2,9946420,0,,100000000,2059136, diff --git a/results_rpi/raw_10000.txt b/results_rpi/raw_10000.txt new file mode 100644 index 0000000..835e949 --- /dev/null +++ b/results_rpi/raw_10000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 4.0 MB | tick_mean= 0.0 µs | ingest= 97.0 sim= 27.0 export= 14.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 6.3 MB | tick_mean= 284.8 µs | ingest= 97.1 sim= 17.1 export= 14.0 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 7.8 MB | tick_mean= 286.0 µs | ingest= 97.1 sim= 17.2 export= 14.1 diag= 0.1 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 9.3 MB | tick_mean= 286.1 µs | ingest= 97.3 sim= 17.3 export= 14.1 diag= 0.1 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 10.9 MB | tick_mean= 284.8 µs | ingest= 97.0 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 12.4 MB | tick_mean= 284.6 µs | ingest= 97.1 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.430 s +Sustained tick rate: 3497.7 Hz +Per-tick mean (wall): 28.5 µs + +Per-system mean (µs): +IngestSystem: 97.2 +SimulationSystem: 17.2 +ExportSystem: 14.0 +DiagnosticsSystem: 0.1 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 13.1 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_100000.txt b/results_rpi/raw_100000.txt new file mode 100644 index 0000000..15ea4f6 --- /dev/null +++ b/results_rpi/raw_100000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 16.3 MB | tick_mean= 0.0 µs | ingest= 948.0 sim= 607.0 export= 157.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 36.7 MB | tick_mean= 4126.0 µs | ingest= 986.9 sim= 218.2 export= 150.2 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 51.9 MB | tick_mean= 4197.8 µs | ingest= 987.7 sim= 236.6 export= 152.8 diag= 0.1 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 67.2 MB | tick_mean= 4132.4 µs | ingest= 986.1 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 209, act=10000) | faults=3624295 lockouts= 0 | rss= 82.4 MB | tick_mean= 4132.1 µs | ingest= 986.5 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 187, act=10000) | faults=4525217 lockouts= 0 | rss= 97.7 MB | tick_mean= 4132.9 µs | ingest= 985.3 sim= 217.1 export= 150.0 diag= 0.1 µs | exported=3655.69 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 20.732 s +Sustained tick rate: 241.2 Hz +Per-tick mean (wall): 413.0 µs + +Per-system mean (µs): +IngestSystem: 985.7 +SimulationSystem: 217.2 +ExportSystem: 150.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.97 MB +Acks processed: 50000000 +Alerts seen: 1029861 + +Memory (RSS at end): 105.3 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_150000.txt b/results_rpi/raw_150000.txt new file mode 100644 index 0000000..35d7438 --- /dev/null +++ b/results_rpi/raw_150000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 24.5 MB | tick_mean= 0.0 µs | ingest=1444.0 sim= 884.0 export= 251.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 306, act=15000) | faults=1385323 lockouts= 0 | rss= 54.3 MB | tick_mean= 6453.6 µs | ingest=1517.1 sim= 381.6 export= 226.7 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 279, act=15000) | faults=2736064 lockouts= 0 | rss= 77.2 MB | tick_mean= 6469.3 µs | ingest=1517.2 sim= 381.6 export= 226.8 diag= 0.1 µs | exported=1828.57 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 317, act=15000) | faults=4087020 lockouts= 0 | rss= 100.1 MB | tick_mean= 6473.9 µs | ingest=1519.6 sim= 382.4 export= 227.0 diag= 0.1 µs | exported=3046.87 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 320, act=15000) | faults=5437256 lockouts= 0 | rss= 123.0 MB | tick_mean= 6470.4 µs | ingest=1518.3 sim= 381.8 export= 226.7 diag= 0.1 µs | exported=4265.17 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 323, act=15000) | faults=6786973 lockouts= 0 | rss= 145.8 MB | tick_mean= 6470.2 µs | ingest=1518.6 sim= 382.0 export= 226.7 diag= 0.1 µs | exported=5483.48 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 32.355 s +Sustained tick rate: 154.5 Hz +Per-tick mean (wall): 646.7 µs + +Per-system mean (µs): +IngestSystem: 1517.3 +SimulationSystem: 381.4 +ExportSystem: 226.6 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.41 MB +Acks processed: 75000000 +Alerts seen: 1538845 + +Memory (RSS at end): 157.2 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_200000.txt b/results_rpi/raw_200000.txt new file mode 100644 index 0000000..82d8c1f --- /dev/null +++ b/results_rpi/raw_200000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 30.0 MB | tick_mean= 0.0 µs | ingest=1907.0 sim=1336.0 export= 349.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 405, act=20000) | faults=1847083 lockouts= 0 | rss= 69.5 MB | tick_mean= 8788.2 µs | ingest=2023.3 sim= 582.9 export= 300.9 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 396, act=20000) | faults=3647521 lockouts= 0 | rss= 100.0 MB | tick_mean= 8805.3 µs | ingest=2023.3 sim= 582.9 export= 300.5 diag= 0.1 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 408, act=20000) | faults=5447700 lockouts= 0 | rss= 130.5 MB | tick_mean= 8805.5 µs | ingest=2023.7 sim= 582.7 export= 300.4 diag= 0.1 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 444, act=20000) | faults=7247668 lockouts= 0 | rss= 161.1 MB | tick_mean= 8808.2 µs | ingest=2024.8 sim= 583.4 export= 300.7 diag= 0.2 µs | exported=5686.94 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 429, act=20000) | faults=9047286 lockouts= 0 | rss= 191.6 MB | tick_mean= 8804.6 µs | ingest=2022.3 sim= 582.4 export= 300.7 diag= 0.1 µs | exported=7311.36 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 44.034 s +Sustained tick rate: 113.5 Hz +Per-tick mean (wall): 880.6 µs + +Per-system mean (µs): +IngestSystem: 2022.7 +SimulationSystem: 582.0 +ExportSystem: 300.7 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.94 MB +Acks processed: 100000000 +Alerts seen: 2059136 + +Memory (RSS at end): 206.8 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_25000.txt b/results_rpi/raw_25000.txt new file mode 100644 index 0000000..faa38ce --- /dev/null +++ b/results_rpi/raw_25000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 6.0 MB | tick_mean= 0.0 µs | ingest= 236.0 sim= 81.0 export= 36.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 11.6 MB | tick_mean= 812.9 µs | ingest= 243.3 sim= 43.2 export= 35.1 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 15.5 MB | tick_mean= 812.1 µs | ingest= 244.1 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 19.2 MB | tick_mean= 811.6 µs | ingest= 243.6 sim= 43.0 export= 35.1 diag= 0.1 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 23.1 MB | tick_mean= 814.2 µs | ingest= 244.0 sim= 43.3 export= 35.1 diag= 0.1 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 26.9 MB | tick_mean= 811.4 µs | ingest= 243.6 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.066 s +Sustained tick rate: 1229.7 Hz +Per-tick mean (wall): 81.3 µs + +Per-system mean (µs): +IngestSystem: 244.0 +SimulationSystem: 43.2 +ExportSystem: 35.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 28.8 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_50000.txt b/results_rpi/raw_50000.txt new file mode 100644 index 0000000..e4e5682 --- /dev/null +++ b/results_rpi/raw_50000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 9.4 MB | tick_mean= 0.0 µs | ingest= 528.0 sim= 211.0 export= 73.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 20.1 MB | tick_mean= 1918.9 µs | ingest= 497.0 sim= 94.6 export= 70.9 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 27.7 MB | tick_mean= 1922.8 µs | ingest= 496.6 sim= 94.4 export= 71.0 diag= 0.1 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 35.3 MB | tick_mean= 1924.0 µs | ingest= 497.0 sim= 94.7 export= 71.0 diag= 0.1 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 43.0 MB | tick_mean= 1922.6 µs | ingest= 496.6 sim= 94.5 export= 70.8 diag= 0.1 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 50.6 MB | tick_mean= 1921.7 µs | ingest= 496.7 sim= 94.5 export= 70.9 diag= 0.1 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 9.617 s +Sustained tick rate: 519.9 Hz +Per-tick mean (wall): 192.0 µs + +Per-system mean (µs): +IngestSystem: 496.0 +SimulationSystem: 94.5 +ExportSystem: 70.8 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 54.3 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/raw_75000.txt b/results_rpi/raw_75000.txt new file mode 100644 index 0000000..20b48aa --- /dev/null +++ b/results_rpi/raw_75000.txt @@ -0,0 +1,35 @@ +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 13.6 MB | tick_mean= 0.0 µs | ingest= 713.0 sim= 383.0 export= 112.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 142, act= 7500) | faults=693165 lockouts= 0 | rss= 28.7 MB | tick_mean= 3016.1 µs | ingest= 735.8 sim= 156.6 export= 110.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 166, act= 7500) | faults=1367793 lockouts= 0 | rss= 40.1 MB | tick_mean= 3021.4 µs | ingest= 735.8 sim= 156.7 export= 110.8 diag= 0.1 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 159, act= 7500) | faults=2042643 lockouts= 0 | rss= 51.5 MB | tick_mean= 3024.8 µs | ingest= 736.4 sim= 156.9 export= 110.8 diag= 0.1 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 166, act= 7500) | faults=2717674 lockouts= 0 | rss= 63.0 MB | tick_mean= 3022.6 µs | ingest= 735.3 sim= 156.9 export= 110.8 diag= 0.2 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 155, act= 7500) | faults=3393495 lockouts= 0 | rss= 74.4 MB | tick_mean= 3021.8 µs | ingest= 735.2 sim= 156.5 export= 110.8 diag= 0.2 µs | exported=2741.79 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 15.118 s +Sustained tick rate: 330.7 Hz +Per-tick mean (wall): 302.3 µs + +Per-system mean (µs): +IngestSystem: 736.2 +SimulationSystem: 156.6 +ExportSystem: 110.7 +DiagnosticsSystem: 0.3 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.74 MB +Acks processed: 37500000 +Alerts seen: 773395 + +Memory (RSS at end): 80.1 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/summary.txt b/results_rpi/summary.txt new file mode 100644 index 0000000..dc837b6 --- /dev/null +++ b/results_rpi/summary.txt @@ -0,0 +1,283 @@ +ECS DT Benchmark Sweep — Tue 21 Apr 01:12:15 UTC 2026 +=== System Information === +Date: 2026-04-21 01:12:15 UTC +OS: Linux 6.12.75+rpt-rpi-2712 +Arch: aarch64 +Target CPU: cortex-a76 +Rust: rustc 1.95.0 (59807616e 2026-04-14) +Cargo: cargo 1.95.0 (f2d3ce0bd 2026-03-21) + +=== CPU Info === +CPU implementer : 0x41 +CPU part : 0xd0b +Revision : c04180 + +=== Memory === +MemTotal: 4146896 kB +MemAvailable: 3883072 kB + +=== CPU Governor === + /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor: performance + +=== Isolated cores === + + + +=== entities=10000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=10000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=7000 EventAsset=2000 ActuatorAsset=1000 total=10000 +[warmup] running 500 ticks... +tick= 0 | entities= 10000 (sensor_active= 8817, alerts= 0, act= 1000) | faults= 183 lockouts= 0 | rss= 4.0 MB | tick_mean= 0.0 µs | ingest= 97.0 sim= 27.0 export= 14.0 diag= 0.0 µs | exported=0.08 MB acks=1000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 10000 (sensor_active= 4488, alerts= 23, act= 1000) | faults= 92482 lockouts= 0 | rss= 6.3 MB | tick_mean= 284.8 µs | ingest= 97.1 sim= 17.1 export= 14.0 diag= 0.0 µs | exported=40.70 MB acks=501000 +tick= 2000 | entities= 10000 (sensor_active= 4425, alerts= 23, act= 1000) | faults=182979 lockouts= 0 | rss= 7.8 MB | tick_mean= 286.0 µs | ingest= 97.1 sim= 17.2 export= 14.1 diag= 0.1 µs | exported=121.92 MB acks=1501000 +tick= 3000 | entities= 10000 (sensor_active= 4507, alerts= 27, act= 1000) | faults=273085 lockouts= 0 | rss= 9.3 MB | tick_mean= 286.1 µs | ingest= 97.3 sim= 17.3 export= 14.1 diag= 0.1 µs | exported=203.15 MB acks=2501000 +tick= 4000 | entities= 10000 (sensor_active= 4502, alerts= 15, act= 1000) | faults=363376 lockouts= 0 | rss= 10.9 MB | tick_mean= 284.8 µs | ingest= 97.0 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=284.37 MB acks=3501000 +tick= 5000 | entities= 10000 (sensor_active= 4527, alerts= 24, act= 1000) | faults=453183 lockouts= 0 | rss= 12.4 MB | tick_mean= 284.6 µs | ingest= 97.1 sim= 17.2 export= 14.0 diag= 0.1 µs | exported=365.59 MB acks=4501000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 1.430 s +Sustained tick rate: 3497.7 Hz +Per-tick mean (wall): 28.5 µs + +Per-system mean (µs): +IngestSystem: 97.2 +SimulationSystem: 17.2 +ExportSystem: 14.0 +DiagnosticsSystem: 0.1 + +Fault injection: +Total sensor faults: 498016 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 406.11 MB +Acks processed: 5000000 +Alerts seen: 104943 + +Memory (RSS at end): 13.1 MB +──────────────────────────────────────────────────────── + +=== entities=25000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=25000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=17500 EventAsset=5000 ActuatorAsset=2500 total=25000 +[warmup] running 500 ticks... +tick= 0 | entities= 25000 (sensor_active= 22049, alerts= 0, act= 2500) | faults= 451 lockouts= 0 | rss= 6.0 MB | tick_mean= 0.0 µs | ingest= 236.0 sim= 81.0 export= 36.0 diag= 0.0 µs | exported=0.20 MB acks=2500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 25000 (sensor_active= 11440, alerts= 49, act= 2500) | faults=231419 lockouts= 0 | rss= 11.6 MB | tick_mean= 812.9 µs | ingest= 243.3 sim= 43.2 export= 35.1 diag= 0.0 µs | exported=101.74 MB acks=1252500 +tick= 2000 | entities= 25000 (sensor_active= 11301, alerts= 43, act= 2500) | faults=456552 lockouts= 0 | rss= 15.5 MB | tick_mean= 812.1 µs | ingest= 244.1 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=304.78 MB acks=3752500 +tick= 3000 | entities= 25000 (sensor_active= 11421, alerts= 58, act= 2500) | faults=681247 lockouts= 0 | rss= 19.2 MB | tick_mean= 811.6 µs | ingest= 243.6 sim= 43.0 export= 35.1 diag= 0.1 µs | exported=507.82 MB acks=6252500 +tick= 4000 | entities= 25000 (sensor_active= 11157, alerts= 48, act= 2500) | faults=906865 lockouts= 0 | rss= 23.1 MB | tick_mean= 814.2 µs | ingest= 244.0 sim= 43.3 export= 35.1 diag= 0.1 µs | exported=710.88 MB acks=8752500 +tick= 5000 | entities= 25000 (sensor_active= 11216, alerts= 49, act= 2500) | faults=1131163 lockouts= 0 | rss= 26.9 MB | tick_mean= 811.4 µs | ingest= 243.6 sim= 43.2 export= 35.1 diag= 0.1 µs | exported=913.91 MB acks=11252500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 4.066 s +Sustained tick rate: 1229.7 Hz +Per-tick mean (wall): 81.3 µs + +Per-system mean (µs): +IngestSystem: 244.0 +SimulationSystem: 43.2 +ExportSystem: 35.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 1243639 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 1015.22 MB +Acks processed: 12500000 +Alerts seen: 254654 + +Memory (RSS at end): 28.8 MB +──────────────────────────────────────────────────────── + +=== entities=50000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=50000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=35000 EventAsset=10000 ActuatorAsset=5000 total=50000 +[warmup] running 500 ticks... +tick= 0 | entities= 50000 (sensor_active= 44082, alerts= 0, act= 5000) | faults= 918 lockouts= 0 | rss= 9.4 MB | tick_mean= 0.0 µs | ingest= 528.0 sim= 211.0 export= 73.0 diag= 0.0 µs | exported=0.41 MB acks=5000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 50000 (sensor_active= 22364, alerts= 118, act= 5000) | faults=462345 lockouts= 0 | rss= 20.1 MB | tick_mean= 1918.9 µs | ingest= 497.0 sim= 94.6 export= 70.9 diag= 0.0 µs | exported=203.46 MB acks=2505000 +tick= 2000 | entities= 50000 (sensor_active= 22510, alerts= 97, act= 5000) | faults=912440 lockouts= 0 | rss= 27.7 MB | tick_mean= 1922.8 µs | ingest= 496.6 sim= 94.4 export= 71.0 diag= 0.1 µs | exported=609.56 MB acks=7505000 +tick= 3000 | entities= 50000 (sensor_active= 22512, alerts= 90, act= 5000) | faults=1361728 lockouts= 0 | rss= 35.3 MB | tick_mean= 1924.0 µs | ingest= 497.0 sim= 94.7 export= 71.0 diag= 0.1 µs | exported=1015.66 MB acks=12505000 +tick= 4000 | entities= 50000 (sensor_active= 22519, alerts= 98, act= 5000) | faults=1812084 lockouts= 0 | rss= 43.0 MB | tick_mean= 1922.6 µs | ingest= 496.6 sim= 94.5 export= 70.8 diag= 0.1 µs | exported=1421.74 MB acks=17505000 +tick= 5000 | entities= 50000 (sensor_active= 22355, alerts= 101, act= 5000) | faults=2261407 lockouts= 0 | rss= 50.6 MB | tick_mean= 1921.7 µs | ingest= 496.7 sim= 94.5 export= 70.9 diag= 0.1 µs | exported=1827.82 MB acks=22505000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 9.617 s +Sustained tick rate: 519.9 Hz +Per-tick mean (wall): 192.0 µs + +Per-system mean (µs): +IngestSystem: 496.0 +SimulationSystem: 94.5 +ExportSystem: 70.8 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 2486121 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 2030.46 MB +Acks processed: 25000000 +Alerts seen: 511654 + +Memory (RSS at end): 54.3 MB +──────────────────────────────────────────────────────── + +=== entities=75000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=75000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=52500 EventAsset=15000 ActuatorAsset=7500 total=75000 +[warmup] running 500 ticks... +tick= 0 | entities= 75000 (sensor_active= 66146, alerts= 0, act= 7500) | faults= 1354 lockouts= 0 | rss= 13.6 MB | tick_mean= 0.0 µs | ingest= 713.0 sim= 383.0 export= 112.0 diag= 0.0 µs | exported=0.61 MB acks=7500 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 75000 (sensor_active= 33877, alerts= 142, act= 7500) | faults=693165 lockouts= 0 | rss= 28.7 MB | tick_mean= 3016.1 µs | ingest= 735.8 sim= 156.6 export= 110.8 diag= 0.0 µs | exported=305.18 MB acks=3757500 +tick= 2000 | entities= 75000 (sensor_active= 33696, alerts= 166, act= 7500) | faults=1367793 lockouts= 0 | rss= 40.1 MB | tick_mean= 3021.4 µs | ingest= 735.8 sim= 156.7 export= 110.8 diag= 0.1 µs | exported=914.34 MB acks=11257500 +tick= 3000 | entities= 75000 (sensor_active= 33889, alerts= 159, act= 7500) | faults=2042643 lockouts= 0 | rss= 51.5 MB | tick_mean= 3024.8 µs | ingest= 736.4 sim= 156.9 export= 110.8 diag= 0.1 µs | exported=1523.51 MB acks=18757500 +tick= 4000 | entities= 75000 (sensor_active= 33657, alerts= 166, act= 7500) | faults=2717674 lockouts= 0 | rss= 63.0 MB | tick_mean= 3022.6 µs | ingest= 735.3 sim= 156.9 export= 110.8 diag= 0.2 µs | exported=2132.66 MB acks=26257500 +tick= 5000 | entities= 75000 (sensor_active= 33573, alerts= 155, act= 7500) | faults=3393495 lockouts= 0 | rss= 74.4 MB | tick_mean= 3021.8 µs | ingest= 735.2 sim= 156.5 export= 110.8 diag= 0.2 µs | exported=2741.79 MB acks=33757500 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 15.118 s +Sustained tick rate: 330.7 Hz +Per-tick mean (wall): 302.3 µs + +Per-system mean (µs): +IngestSystem: 736.2 +SimulationSystem: 156.6 +ExportSystem: 110.7 +DiagnosticsSystem: 0.3 + +Fault injection: +Total sensor faults: 3730093 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 3045.74 MB +Acks processed: 37500000 +Alerts seen: 773395 + +Memory (RSS at end): 80.1 MB +──────────────────────────────────────────────────────── + +=== entities=100000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=100000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=70000 EventAsset=20000 ActuatorAsset=10000 total=100000 +[warmup] running 500 ticks... +tick= 0 | entities= 100000 (sensor_active= 88228, alerts= 0, act=10000) | faults= 1772 lockouts= 0 | rss= 16.3 MB | tick_mean= 0.0 µs | ingest= 948.0 sim= 607.0 export= 157.0 diag= 0.0 µs | exported=0.81 MB acks=10000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 100000 (sensor_active= 45396, alerts= 218, act=10000) | faults=924085 lockouts= 0 | rss= 36.7 MB | tick_mean= 4126.0 µs | ingest= 986.9 sim= 218.2 export= 150.2 diag= 0.0 µs | exported=406.92 MB acks=5010000 +tick= 2000 | entities= 100000 (sensor_active= 44998, alerts= 212, act=10000) | faults=1823730 lockouts= 0 | rss= 51.9 MB | tick_mean= 4197.8 µs | ingest= 987.7 sim= 236.6 export= 152.8 diag= 0.1 µs | exported=1219.14 MB acks=15010000 +tick= 3000 | entities= 100000 (sensor_active= 45132, alerts= 187, act=10000) | faults=2723608 lockouts= 0 | rss= 67.2 MB | tick_mean= 4132.4 µs | ingest= 986.1 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2031.31 MB acks=25010000 +tick= 4000 | entities= 100000 (sensor_active= 45039, alerts= 209, act=10000) | faults=3624295 lockouts= 0 | rss= 82.4 MB | tick_mean= 4132.1 µs | ingest= 986.5 sim= 217.8 export= 150.1 diag= 0.1 µs | exported=2843.48 MB acks=35010000 +tick= 5000 | entities= 100000 (sensor_active= 45069, alerts= 187, act=10000) | faults=4525217 lockouts= 0 | rss= 97.7 MB | tick_mean= 4132.9 µs | ingest= 985.3 sim= 217.1 export= 150.0 diag= 0.1 µs | exported=3655.69 MB acks=45010000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 20.732 s +Sustained tick rate: 241.2 Hz +Per-tick mean (wall): 413.0 µs + +Per-system mean (µs): +IngestSystem: 985.7 +SimulationSystem: 217.2 +ExportSystem: 150.1 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 4974449 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 4060.97 MB +Acks processed: 50000000 +Alerts seen: 1029861 + +Memory (RSS at end): 105.3 MB +──────────────────────────────────────────────────────── + +=== entities=150000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=150000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=105000 EventAsset=30000 ActuatorAsset=15000 total=150000 +[warmup] running 500 ticks... +tick= 0 | entities= 150000 (sensor_active=132379, alerts= 0, act=15000) | faults= 2621 lockouts= 0 | rss= 24.5 MB | tick_mean= 0.0 µs | ingest=1444.0 sim= 884.0 export= 251.0 diag= 0.0 µs | exported=1.22 MB acks=15000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 150000 (sensor_active= 67721, alerts= 306, act=15000) | faults=1385323 lockouts= 0 | rss= 54.3 MB | tick_mean= 6453.6 µs | ingest=1517.1 sim= 381.6 export= 226.7 diag= 0.0 µs | exported=610.34 MB acks=7515000 +tick= 2000 | entities= 150000 (sensor_active= 67238, alerts= 279, act=15000) | faults=2736064 lockouts= 0 | rss= 77.2 MB | tick_mean= 6469.3 µs | ingest=1517.2 sim= 381.6 export= 226.8 diag= 0.1 µs | exported=1828.57 MB acks=22515000 +tick= 3000 | entities= 150000 (sensor_active= 67728, alerts= 317, act=15000) | faults=4087020 lockouts= 0 | rss= 100.1 MB | tick_mean= 6473.9 µs | ingest=1519.6 sim= 382.4 export= 227.0 diag= 0.1 µs | exported=3046.87 MB acks=37515000 +tick= 4000 | entities= 150000 (sensor_active= 67533, alerts= 320, act=15000) | faults=5437256 lockouts= 0 | rss= 123.0 MB | tick_mean= 6470.4 µs | ingest=1518.3 sim= 381.8 export= 226.7 diag= 0.1 µs | exported=4265.17 MB acks=52515000 +tick= 5000 | entities= 150000 (sensor_active= 67700, alerts= 323, act=15000) | faults=6786973 lockouts= 0 | rss= 145.8 MB | tick_mean= 6470.2 µs | ingest=1518.6 sim= 382.0 export= 226.7 diag= 0.1 µs | exported=5483.48 MB acks=67515000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 32.355 s +Sustained tick rate: 154.5 Hz +Per-tick mean (wall): 646.7 µs + +Per-system mean (µs): +IngestSystem: 1517.3 +SimulationSystem: 381.4 +ExportSystem: 226.6 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 7460636 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 6091.41 MB +Acks processed: 75000000 +Alerts seen: 1538845 + +Memory (RSS at end): 157.2 MB +──────────────────────────────────────────────────────── + +=== entities=200000 === +ECS Digital Twin Benchmark — TRANSIT lab / UQAC +entities=200000 | ticks=5000 | warmup=500 | fault_prob=2.0% +[spawn] SensorAsset=140000 EventAsset=40000 ActuatorAsset=20000 total=200000 +[warmup] running 500 ticks... +tick= 0 | entities= 200000 (sensor_active=176493, alerts= 0, act=20000) | faults= 3507 lockouts= 0 | rss= 30.0 MB | tick_mean= 0.0 µs | ingest=1907.0 sim=1336.0 export= 349.0 diag= 0.0 µs | exported=1.62 MB acks=20000 +[warmup] done — starting measurement (5000 ticks) +tick= 1000 | entities= 200000 (sensor_active= 90145, alerts= 405, act=20000) | faults=1847083 lockouts= 0 | rss= 69.5 MB | tick_mean= 8788.2 µs | ingest=2023.3 sim= 582.9 export= 300.9 diag= 0.0 µs | exported=813.81 MB acks=10020000 +tick= 2000 | entities= 200000 (sensor_active= 90392, alerts= 396, act=20000) | faults=3647521 lockouts= 0 | rss= 100.0 MB | tick_mean= 8805.3 µs | ingest=2023.3 sim= 582.9 export= 300.5 diag= 0.1 µs | exported=2438.20 MB acks=30020000 +tick= 3000 | entities= 200000 (sensor_active= 90372, alerts= 408, act=20000) | faults=5447700 lockouts= 0 | rss= 130.5 MB | tick_mean= 8805.5 µs | ingest=2023.7 sim= 582.7 export= 300.4 diag= 0.1 µs | exported=4062.57 MB acks=50020000 +tick= 4000 | entities= 200000 (sensor_active= 89452, alerts= 444, act=20000) | faults=7247668 lockouts= 0 | rss= 161.1 MB | tick_mean= 8808.2 µs | ingest=2024.8 sim= 583.4 export= 300.7 diag= 0.2 µs | exported=5686.94 MB acks=70020000 +tick= 5000 | entities= 200000 (sensor_active= 89914, alerts= 429, act=20000) | faults=9047286 lockouts= 0 | rss= 191.6 MB | tick_mean= 8804.6 µs | ingest=2022.3 sim= 582.4 export= 300.7 diag= 0.1 µs | exported=7311.36 MB acks=90020000 + +── Final Summary ────────────────────────────────────── +Total ticks (measurement): 5000 +Total wall time: 44.034 s +Sustained tick rate: 113.5 Hz +Per-tick mean (wall): 880.6 µs + +Per-system mean (µs): +IngestSystem: 2022.7 +SimulationSystem: 582.0 +ExportSystem: 300.7 +DiagnosticsSystem: 0.2 + +Fault injection: +Total sensor faults: 9946420 +Total actuator lockouts: 0 + +Export totals: +Bytes exported: 8121.94 MB +Acks processed: 100000000 +Alerts seen: 2059136 + +Memory (RSS at end): 206.8 MB +──────────────────────────────────────────────────────── diff --git a/results_rpi/sysinfo.txt b/results_rpi/sysinfo.txt new file mode 100644 index 0000000..e23a91a --- /dev/null +++ b/results_rpi/sysinfo.txt @@ -0,0 +1,22 @@ +=== System Information === +Date: 2026-04-21 01:12:15 UTC +OS: Linux 6.12.75+rpt-rpi-2712 +Arch: aarch64 +Target CPU: cortex-a76 +Rust: rustc 1.95.0 (59807616e 2026-04-14) +Cargo: cargo 1.95.0 (f2d3ce0bd 2026-03-21) + +=== CPU Info === +CPU implementer : 0x41 +CPU part : 0xd0b +Revision : c04180 + +=== Memory === +MemTotal: 4146896 kB +MemAvailable: 3883072 kB + +=== CPU Governor === + /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor: performance + +=== Isolated cores === + diff --git a/run_benchmark.sh b/run_benchmark.sh new file mode 100755 index 0000000..b2589a8 --- /dev/null +++ b/run_benchmark.sh @@ -0,0 +1,265 @@ +#!/usr/bin/env bash +# ============================================================= +# ECS Digital Twin Benchmark — build + full sweep +# TRANSIT lab / UQAC +# +# Detects the host CPU, builds with native hardware optimizations, +# then runs the full entity sweep for the paper tables. +# +# Usage: +# chmod +x run_benchmark.sh +# ./run_benchmark.sh +# +# Output: +# results/summary.txt — human-readable full log +# results/csv/ — one CSV per entity count (for paper tables) +# results/final_table.csv — aggregated table ready for LaTeX +# ============================================================= + +set -euo pipefail + +# ── Paths ───────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RESULTS_DIR="${SCRIPT_DIR}/results" +CSV_DIR="${RESULTS_DIR}/csv" +BINARY="${SCRIPT_DIR}/target/release/ecs_dt_benchmark" + +mkdir -p "${RESULTS_DIR}" "${CSV_DIR}" + +# ── Benchmark parameters ─────────────────────────────────────── +ENTITY_COUNTS=(10000 25000 50000 75000 100000 150000 200000) +TICKS=5000 +WARMUP=500 +FAULT_PROB=0.02 # 2 % per tick per entity +SEED=17446744073709551615 + +# ── Platform detection ──────────────────────────────────────── +ARCH="$(uname -m)" +OS="$(uname -s)" + +echo "============================================================" +echo " ECS Digital Twin Benchmark — TRANSIT lab / UQAC" +echo " Platform: ${OS} / ${ARCH}" +echo "============================================================" + +# Detect the best target-cpu flag for this machine. +detect_target_cpu() { + case "${ARCH}" in + aarch64) + # On RPi 5 (Cortex-A76) /proc/cpuinfo lists "CPU part : 0xd0b" + # On Apple M-series (native aarch64 via Rosetta or native build) + # uname gives arm64 on macOS, aarch64 on Linux. + if [[ "${OS}" == "Darwin" ]]; then + # Apple Silicon — the toolchain knows the exact µarch. + echo "apple-m1" + elif grep -q "Cortex-A76" /proc/cpuinfo 2>/dev/null || \ + grep -q "0xd0b" /proc/cpuinfo 2>/dev/null; then + echo "cortex-a76" # RPi 5 + elif grep -q "Cortex-A72" /proc/cpuinfo 2>/dev/null || \ + grep -q "0xd08" /proc/cpuinfo 2>/dev/null; then + echo "cortex-a72" # RPi 4 + else + echo "native" # fall back: let LLVM auto-detect + fi + ;; + x86_64) + echo "native" # covers Ryzen, Intel, any x86-64 + ;; + arm64) + # macOS reports arm64 + echo "apple-m1" + ;; + *) + echo "native" + ;; + esac +} + +TARGET_CPU="$(detect_target_cpu)" +echo " Target CPU flag: -C target-cpu=${TARGET_CPU}" + +# ── CPU isolation advice ─────────────────────────────────────── +# On Linux, pin to an isolated core if isolcpus was set at boot. +# On the RPi 5 for the paper we recommend: +# sudo systemctl isolate multi-user.target (drop GUI) +# TASKSET="taskset -c 0" +# We auto-detect isolated cores from the kernel cmdline. +TASKSET="" +if [[ "${OS}" == "Linux" ]]; then + ISOLATED="" + if [[ -r /sys/devices/system/cpu/isolated ]]; then + ISOLATED="$(cat /sys/devices/system/cpu/isolated)" + fi + if [[ -n "${ISOLATED}" ]]; then + # Use the first isolated core. + FIRST_CORE="${ISOLATED%%[-,]*}" + TASKSET="taskset -c ${FIRST_CORE}" + echo " Isolated cores: ${ISOLATED} → pinning to core ${FIRST_CORE}" + else + echo " No isolated cores detected — running unpinned." + echo " Tip: add isolcpus=2 nohz_full=2 rcu_nocbs=2 to /boot/cmdline.txt" + echo " for more stable single-core measurements on RPi 5." + fi +fi + +# ── Build ────────────────────────────────────────────────────── +echo "" +echo "── Building (release + native optimizations) ────────────" + +RUSTFLAGS="-C target-cpu=${TARGET_CPU} -C opt-level=3" \ + cargo build --release 2>&1 | tail -5 + +echo " Binary: ${BINARY}" +echo " Size: $(du -sh "${BINARY}" | cut -f1)" + +# ── System info snapshot ─────────────────────────────────────── +SYSINFO_FILE="${RESULTS_DIR}/sysinfo.txt" +{ + echo "=== System Information ===" + echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + echo "OS: ${OS} $(uname -r 2>/dev/null || true)" + echo "Arch: ${ARCH}" + echo "Target CPU: ${TARGET_CPU}" + echo "Rust: $(rustc --version)" + echo "Cargo: $(cargo --version)" + echo "" + if [[ "${OS}" == "Linux" ]]; then + echo "=== CPU Info ===" + grep -E "^(model name|Hardware|CPU part|CPU implementer|Revision)" \ + /proc/cpuinfo 2>/dev/null | sort -u || true + echo "" + echo "=== Memory ===" + grep -E "^(MemTotal|MemAvailable)" /proc/meminfo 2>/dev/null || true + echo "" + echo "=== CPU Governor ===" + for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + [[ -r "$f" ]] && echo " $f: $(cat "$f")" && break + done + echo "" + echo "=== Isolated cores ===" + cat /sys/devices/system/cpu/isolated 2>/dev/null || echo " none" + elif [[ "${OS}" == "Darwin" ]]; then + sysctl -n machdep.cpu.brand_string 2>/dev/null || true + sysctl -n hw.memsize 2>/dev/null | \ + awk '{printf "Memory: %.1f GB\n", $1/1073741824}' || true + fi +} | tee "${SYSINFO_FILE}" + +echo "" + +# ── CSV header ──────────────────────────────────────────────── +FINAL_CSV="${RESULTS_DIR}/final_table.csv" +echo "entities,ticks,wall_s,hz,per_tick_us,ingest_us,sim_us,export_us,diag_us,\ +sensor_faults,actuator_lockouts,exported_mb,acks,alerts,rss_mb" \ + > "${FINAL_CSV}" + +# ── Sweep ───────────────────────────────────────────────────── +echo "── Running entity sweep ─────────────────────────────────" +echo " Counts: ${ENTITY_COUNTS[*]}" +echo " Ticks: ${TICKS} Warmup: ${WARMUP} Fault: ${FAULT_PROB}" +echo "" + +SUMMARY_FILE="${RESULTS_DIR}/summary.txt" +echo "ECS DT Benchmark Sweep — $(date -u)" > "${SUMMARY_FILE}" +cat "${SYSINFO_FILE}" >> "${SUMMARY_FILE}" +echo "" >> "${SUMMARY_FILE}" + +for N in "${ENTITY_COUNTS[@]}"; do + echo "── entities=${N} ─────────────────────────────────────" + + RAW_FILE="${RESULTS_DIR}/raw_${N}.txt" + + # Run benchmark, tee to file and stdout. + # shellcheck disable=SC2086 + ${TASKSET} "${BINARY}" \ + --entities "${N}" \ + --ticks "${TICKS}" \ + --seed "${SEED}" \ + --fault-prob "${FAULT_PROB}" \ + 2>&1 | tee "${RAW_FILE}" + + echo "" >> "${SUMMARY_FILE}" + echo "=== entities=${N} ===" >> "${SUMMARY_FILE}" + cat "${RAW_FILE}" >> "${SUMMARY_FILE}" + + # ── Parse the Final Summary block from the binary output ── + # Extract values using grep + awk on the known output format. + parse() { + grep -m1 "$1" "${RAW_FILE}" | awk '{print $NF}' + } + + WALL_S=$(grep "Total wall time" "${RAW_FILE}" | awk '{print $NF}' | tr -d 's') + HZ=$(grep "Sustained tick rate" "${RAW_FILE}" | awk '{print $NF}') + PTW=$(grep "Per-tick mean" "${RAW_FILE}" | awk '{print $NF}') + INGEST=$(grep "IngestSystem:" "${RAW_FILE}" | tail -1 | awk '{print $NF}') + SIM=$(grep "SimulationSystem:" "${RAW_FILE}" | tail -1 | awk '{print $NF}') + EXPORT=$(grep "ExportSystem:" "${RAW_FILE}" | tail -1 | awk '{print $NF}') + DIAGSYS=$(grep "DiagnosticsSystem:" "${RAW_FILE}" | tail -1 | awk '{print $NF}') + FAULTS=$(grep "Total sensor faults:" "${RAW_FILE}" | awk '{print $NF}') + LOCKOUTS=$(grep "Total actuator lockouts" "${RAW_FILE}" | awk '{print $NF}') + EXPMT=$(grep "Bytes exported:" "${RAW_FILE}" | awk '{print $NF}') + ACKS=$(grep "Acks processed:" "${RAW_FILE}" | awk '{print $NF}') + ALTS=$(grep "Alerts seen:" "${RAW_FILE}" | awk '{print $NF}') + RSS=$(grep "Memory (RSS" "${RAW_FILE}" | awk '{print $NF}') + + # Remove trailing units that awk picked up (MB, Hz, µs, s). + strip() { echo "$1" | tr -d 'MBHzµs '; } + + echo "${N},${TICKS},$(strip "$WALL_S"),$(strip "$HZ"),$(strip "$PTW"),\ +$(strip "$INGEST"),$(strip "$SIM"),$(strip "$EXPORT"),$(strip "$DIAGSYS"),\ +$(strip "$FAULTS"),$(strip "$LOCKOUTS"),$(strip "$EXPMT"),\ +$(strip "$ACKS"),$(strip "$ALTS"),$(strip "$RSS")" \ + >> "${FINAL_CSV}" + + # Per-entity CSV for detailed analysis. + cat "${RAW_FILE}" > "${CSV_DIR}/entities_${N}.txt" + + echo "" +done + +# ── Pretty-print final table ────────────────────────────────── +echo "============================================================" +echo " Final Results Table" +echo "============================================================" +echo "" +printf "%-10s %-8s %-10s %-12s %-12s %-10s %-10s\n" \ + "Entities" "Hz" "Tick(µs)" "Ingest(µs)" "Sim(µs)" "Export(µs)" "RSS(MB)" +printf "%-10s %-8s %-10s %-12s %-12s %-10s %-10s\n" \ + "--------" "------" "---------" "----------" "--------" "----------" "-------" + +tail -n +2 "${FINAL_CSV}" | while IFS=',' read -r \ + entities ticks wall hz ptw ingest sim export diag \ + faults lockouts expmt acks alts rss; do + printf "%-10s %-8s %-10s %-12s %-12s %-10s %-10s\n" \ + "${entities}" "${hz}" "${ptw}" "${ingest}" "${sim}" "${export}" "${rss}" +done + +echo "" +echo "── Output files ─────────────────────────────────────────" +echo " ${SUMMARY_FILE}" +echo " ${FINAL_CSV}" +echo " ${CSV_DIR}/" +echo "" +echo "── LaTeX snippet for paper Table 2 ─────────────────────" +echo '\begin{table}[h]' +echo '\caption{ECS DT Runtime: Memory and Throughput Scaling (RPi~5, single core)}' +echo '\centering\renewcommand{\arraystretch}{1.25}' +echo '\begin{tabular}{@{}rrrrr@{}}' +echo '\toprule' +echo '\textbf{Entities} & \textbf{RSS (MB)} & \textbf{Tick rate (Hz)} &' +echo '\textbf{Per-tick ($\mu$s)} & \textbf{Sim system ($\mu$s)} \\' +echo '\midrule' + +tail -n +2 "${FINAL_CSV}" | while IFS=',' read -r \ + entities ticks wall hz ptw ingest sim export diag \ + faults lockouts expmt acks alts rss; do + printf '%s & %s & %s & %s & %s \\\\\n' \ + "$(printf '%s' "${entities}" | sed 's/000$/k/' | sed 's/00k$/00k/')" \ + "${rss}" "${hz}" "${ptw}" "${sim}" +done + +echo '\bottomrule' +echo '\end{tabular}' +echo '\end{table}' +echo "" +echo "Done. $(date -u)" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d7bc51e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,935 @@ +/// ============================================================ +/// ECS Digital Twin Benchmark — TRANSIT lab / UQAC +/// Target paper: "ECS as a Natural Runtime Substrate for +/// Industrial Digital Twins" (IEEE SWC 2026) +/// +/// Three asset archetypes mirror the three DT data tiers: +/// Tier 1 — SensorAsset (70 %): ephemeral high-frequency readings +/// Tier 2 — EventAsset (20 %): threshold / fault events +/// Tier 3 — ActuatorAsset (10 %): commanded state with ack tracking +/// +/// Five systems run per tick in a fixed schedule: +/// 1. FaultInjectionSystem — drives archetype mutations +/// 2. IngestSystem — writes fresh sensor / command data +/// 3. SimulationSystem — processes state (heaviest, par_iter) +/// 4. ExportSystem — read-only shadow-model pass +/// 5. DiagnosticsSystem — per-system timing + RSS sampling +/// +/// Usage: +/// cargo run --release -- --entities 100000 --ticks 5000 +/// cargo run --release -- --entities 200000 --ticks 2000 +/// ============================================================ + +use std::{ + collections::HashMap, + fs, + time::{Duration, Instant}, +}; + +use bevy_ecs::prelude::*; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +// ───────────────────────────────────────────────────────────── +// CLI args (no clap dependency — keep compile times short) +// ───────────────────────────────────────────────────────────── + +struct Config { + total_entities: usize, + ticks: u32, + warmup_ticks: u32, + rng_seed: u64, + fault_probability: f32, +} + +impl Default for Config { + fn default() -> Self { + Self { + total_entities: 100_000, + ticks: 5_000, + warmup_ticks: 500, + rng_seed: 0xDEAD_BEEF_CAFE_BABE, + fault_probability: 0.02, + } + } +} + +fn parse_args() -> Config { + let mut cfg = Config::default(); + let args: Vec = std::env::args().collect(); + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "--entities" => { + cfg.total_entities = args[i + 1].parse().expect("bad --entities"); + i += 2; + } + "--ticks" => { + cfg.ticks = args[i + 1].parse().expect("bad --ticks"); + i += 2; + } + "--seed" => { + cfg.rng_seed = args[i + 1].parse().expect("bad --seed"); + i += 2; + } + "--fault-prob" => { + cfg.fault_probability = args[i + 1].parse().expect("bad --fault-prob"); + i += 2; + } + _ => { + i += 1; + } + } + } + cfg +} + +// ───────────────────────────────────────────────────────────── +// Enums +// ───────────────────────────────────────────────────────────── + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum AssetClass { + Sensor, + Event, + Actuator, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SensorUnit { + Temperature, + Vibration, + Position, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum AlertSeverity { + Warning, + Critical, +} + +// ───────────────────────────────────────────────────────────── +// Components — shared +// ───────────────────────────────────────────────────────────── + +/// Pure identity — every entity carries this, no behaviour. +#[derive(Component)] +struct AssetId { + id: u32, + class: AssetClass, +} + +// ───────────────────────────────────────────────────────────── +// Components — Tier 1 SensorAsset +// ───────────────────────────────────────────────────────────── + +/// Written by IngestSystem, read by SimulationSystem. +/// Removed / re-added by FaultInjectionSystem to model sensor dropout. +/// Its *absence* is the fault representation — no null fields, no enums. +#[derive(Component)] +struct RawSensorData { + value: f32, + timestamp_us: u64, + sequence_number: u32, +} + +/// Written by SimulationSystem, read by ExportSystem. +/// Never removed — persists as the last-known-good state even during dropout. +#[derive(Component)] +struct ProcessedState { + filtered_value: f32, + last_updated_tick: u32, +} + +/// Static config — written once at spawn, never mutated. +#[derive(Component)] +struct SensorMetadata { + sample_rate_hz: f32, + unit: SensorUnit, + calibration_offset: f32, +} + +// ───────────────────────────────────────────────────────────── +// Components — Tier 2 EventAsset +// ───────────────────────────────────────────────────────────── + +/// Static threshold config — written once at spawn. +#[derive(Component)] +struct ThresholdConfig { + upper_bound: f32, + lower_bound: f32, + consecutive_violations_required: u8, +} + +/// Mutable per-tick state tracked by SimulationSystem. +#[derive(Component)] +struct ThresholdState { + current_violation_count: u8, + last_violation_tick: u32, + is_alerting: bool, +} + +/// Added by SimulationSystem when threshold is breached. +/// Removed when condition clears. Presence = active alert. +#[derive(Component)] +struct AlertEvent { + severity: AlertSeverity, + triggered_at_tick: u32, + asset_id: u32, +} + +// ───────────────────────────────────────────────────────────── +// Components — Tier 3 ActuatorAsset +// ───────────────────────────────────────────────────────────── + +/// Written by SimulationSystem each tick (lerp toward setpoint). +#[derive(Component)] +struct ActuatorState { + current_setpoint: f32, + actual_position: f32, + last_command_tick: u32, +} + +/// Written by IngestSystem (new command arrives). +/// Cleared by ExportSystem after ack is read. +/// Removed by FaultInjectionSystem when consecutive_failures >= 5. +#[derive(Component)] +struct CommandBuffer { + pending_setpoint: Option, + acknowledged: bool, +} + +/// Written by SimulationSystem when actuator fails to converge. +#[derive(Component)] +struct ActuatorHealth { + fault_code: Option, + consecutive_failures: u8, +} + +// ───────────────────────────────────────────────────────────── +// Resources +// ───────────────────────────────────────────────────────────── + +/// Current tick counter — its own resource so systems can take +/// Res without conflicting with ResMut. +#[derive(Resource, Default)] +struct CurrentTick(u32); + +/// Per-system timing accumulated across the measurement window. +/// Written exclusively by each system that records its own elapsed time, +/// and by DiagnosticsSystem which resets and prints the window. +/// No system takes both Res and ResMut. +#[derive(Resource, Default)] +struct TickDiagnostics { + system_elapsed_us: HashMap<&'static str, u64>, + accumulated_elapsed_us: HashMap<&'static str, u64>, + ticks_in_window: u32, + total_tick_elapsed_us: u64, +} + +impl TickDiagnostics { + fn record(&mut self, name: &'static str, elapsed: Duration) { + let us = elapsed.as_micros() as u64; + self.system_elapsed_us.insert(name, us); + *self.accumulated_elapsed_us.entry(name).or_insert(0) += us; + } +} + +/// Fault injection configuration and deterministic RNG. +#[derive(Resource)] +struct FaultProfile { + fault_probability_per_tick: f32, + rng: ChaCha8Rng, + total_faults_injected: u32, + total_actuator_lockouts: u32, +} + +impl FaultProfile { + fn new(seed: u64, probability: f32) -> Self { + Self { + fault_probability_per_tick: probability, + rng: ChaCha8Rng::seed_from_u64(seed), + total_faults_injected: 0, + total_actuator_lockouts: 0, + } + } +} + +/// Monotonically increasing simulated clock in microseconds. +#[derive(Resource, Default)] +struct SimClock { + current_us: u64, + step_us: u64, +} + +/// Accumulates total "exported" bytes for throughput reporting. +#[derive(Resource, Default)] +struct ExportCounter { + total_bytes: u64, + total_alerts_seen: u32, + total_acks_processed: u32, +} + +// ───────────────────────────────────────────────────────────── +// System labels +// ───────────────────────────────────────────────────────────── + +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +enum DtSystemSet { + Fault, + Ingest, + Simulate, + Export, + Diagnostics, +} + +// ───────────────────────────────────────────────────────────── +// System 1 — FaultInjectionSystem +// +// Drives all archetype mutations. +// Runs BEFORE Ingest and Simulate so they see the updated archetypes. +// +// SensorAsset with RawSensorData → roll to REMOVE it (dropout) +// SensorAsset without RawSensorData → roll to RE-ADD it (recovery) +// ActuatorAsset with consecutive_failures >= 5 → remove CommandBuffer +// ───────────────────────────────────────────────────────────── + +fn fault_injection_system( + mut commands: Commands, + mut profile: ResMut, + active_sensors: Query< + Entity, + (With, With), + >, + inactive_sensors: Query< + (Entity, &SensorMetadata), + (Without, With), + >, + actuators: Query<(Entity, &ActuatorHealth), With>, +) { + let p = profile.fault_probability_per_tick; + + for entity in active_sensors.iter() { + if profile.rng.gen::() < p { + commands.entity(entity).remove::(); + profile.total_faults_injected += 1; + } + } + + for (entity, meta) in inactive_sensors.iter() { + if profile.rng.gen::() < p { + commands.entity(entity).insert(RawSensorData { + value: meta.calibration_offset, + timestamp_us: 0, + sequence_number: 0, + }); + } + } + + for (entity, health) in actuators.iter() { + if health.consecutive_failures >= 5 { + commands.entity(entity).remove::(); + profile.total_actuator_lockouts += 1; + } + } +} + +// ───────────────────────────────────────────────────────────── +// System 2 — IngestSystem +// +// Write set : RawSensorData, CommandBuffer +// Read set : SensorMetadata, SimClock, CurrentTick +// Disjoint from ExportSystem write set → Bevy can schedule in parallel. +// ───────────────────────────────────────────────────────────── + +fn ingest_system( + mut sensors: Query<(&SensorMetadata, &mut RawSensorData)>, + mut actuators: Query<&mut CommandBuffer>, + clock: Res, + mut profile: ResMut, + mut diag: ResMut, +) { + let t0 = Instant::now(); + + for (meta, mut raw) in sensors.iter_mut() { + raw.sequence_number = raw.sequence_number.wrapping_add(1); + let phase = raw.sequence_number as f32 / meta.sample_rate_hz; + raw.value = (phase * std::f32::consts::TAU).sin() + meta.calibration_offset; + raw.timestamp_us = clock.current_us; + } + + let command_probability: f32 = 0.05; + for mut buf in actuators.iter_mut() { + if profile.rng.gen::() < command_probability { + buf.pending_setpoint = Some(profile.rng.gen_range(-1.0_f32..1.0_f32)); + buf.acknowledged = false; + } + } + + diag.record("IngestSystem", t0.elapsed()); +} + +// ───────────────────────────────────────────────────────────── +// System 3 — SimulationSystem +// +// Write set : ProcessedState, ThresholdState, ActuatorState, +// ActuatorHealth, Commands (AlertEvent inserts/removes) +// Read set : RawSensorData, SensorMetadata, ThresholdConfig, +// CommandBuffer, CurrentTick +// +// Takes Res (not TickDiagnostics) to read the tick number, +// then ResMut only to record its elapsed time. +// No Res/ResMut conflict. +// ───────────────────────────────────────────────────────────── + +fn simulation_system( + mut commands: Commands, + tick_res: Res, + // Pass (a): only entities WITH RawSensorData are matched. + // Dropout entities are skipped automatically by the query filter. + mut sensors: Query<( + &RawSensorData, + &SensorMetadata, + &mut ProcessedState, + )>, + // Pass (b): threshold evaluation — also requires RawSensorData. + mut events: Query<( + Entity, + &AssetId, + &RawSensorData, + &ThresholdConfig, + &mut ThresholdState, + Option<&AlertEvent>, + )>, + // Pass (c): actuator convergence. + mut actuators: Query<( + &mut ActuatorState, + &mut ActuatorHealth, + Option<&mut CommandBuffer>, + )>, + mut diag: ResMut, +) { + let t0 = Instant::now(); + let tick = tick_res.0; + let alpha = 0.1_f32; + + // ── (a) Sensor EMA — par_iter_mut, independent per entity ──── + sensors.par_iter_mut().for_each(|(raw, _meta, mut ps)| { + ps.filtered_value = + alpha * raw.value + (1.0 - alpha) * ps.filtered_value; + ps.last_updated_tick = tick; + }); + + // ── (b) Threshold evaluation ────────────────────────────────── + for (entity, id, raw, cfg, mut state, maybe_alert) in events.iter_mut() { + let out_of_bounds = + raw.value > cfg.upper_bound || raw.value < cfg.lower_bound; + + if out_of_bounds { + state.current_violation_count = + state.current_violation_count.saturating_add(1); + state.last_violation_tick = tick; + + if state.current_violation_count >= cfg.consecutive_violations_required + && maybe_alert.is_none() + { + let severity = if state.current_violation_count >= 10 { + AlertSeverity::Critical + } else { + AlertSeverity::Warning + }; + commands.entity(entity).insert(AlertEvent { + severity, + triggered_at_tick: tick, + asset_id: id.id, + }); + state.is_alerting = true; + } + } else { + state.current_violation_count = 0; + if maybe_alert.is_some() { + commands.entity(entity).remove::(); + state.is_alerting = false; + } + } + } + + // ── (c) Actuator convergence ────────────────────────────────── + let lerp_rate = 0.1_f32; + let tolerance = 0.001_f32; + let failure_thresh = 200_u32; + + for (mut state, mut health, maybe_buf) in actuators.iter_mut() { + if let Some(mut buf) = maybe_buf { + if let Some(setpoint) = buf.pending_setpoint { + state.current_setpoint = setpoint; + state.last_command_tick = tick; + } + buf.acknowledged = true; + } + + let delta = state.current_setpoint - state.actual_position; + state.actual_position += delta * lerp_rate; + + if delta.abs() < tolerance { + health.consecutive_failures = 0; + health.fault_code = None; + } else if tick.wrapping_sub(state.last_command_tick) > failure_thresh { + health.consecutive_failures = + health.consecutive_failures.saturating_add(1); + health.fault_code = Some(0x01); + } + } + + diag.record("SimulationSystem", t0.elapsed()); +} + +// ───────────────────────────────────────────────────────────── +// System 4 — ExportSystem +// +// Read set : ProcessedState, AlertEvent, ActuatorState, CommandBuffer +// Write set : CommandBuffer (ack clear), ExportCounter +// +// Entity data writes are limited to CommandBuffer.acknowledged/pending_setpoint. +// ProcessedState and ActuatorState are read-only, so Bevy can overlap +// this system with IngestSystem (which writes RawSensorData only). +// ───────────────────────────────────────────────────────────── + +fn export_system( + processed: Query<&ProcessedState>, + alerts: Query<&AlertEvent>, + // Single query covering all actuator data — avoids the B0001 conflict + // that arises from having separate Res and + // ResMut queries in the same system. + mut actuators: Query<(&ActuatorState, &mut CommandBuffer)>, + // Actuator entities that have lost their CommandBuffer (locked out). + locked_actuators: Query<&ActuatorState, Without>, + mut counter: ResMut, + mut diag: ResMut, +) { + let t0 = Instant::now(); + let mut exported: u64 = 0; + + // Shadow export for all entities with ProcessedState. + for ps in processed.iter() { + let _ = std::hint::black_box((ps.filtered_value, ps.last_updated_tick)); + exported += 8; + } + + // Alert export. + for alert in alerts.iter() { + let _ = std::hint::black_box((alert.severity, alert.triggered_at_tick)); + exported += 8; + counter.total_alerts_seen += 1; + } + + // Actuator shadow export + ack processing in a single pass. + for (state, mut buf) in actuators.iter_mut() { + let _ = std::hint::black_box(state.actual_position); + exported += 4; + if buf.acknowledged { + counter.total_acks_processed += 1; + exported += 1; + // Clear ack post-export — Tier 3 round-trip complete. + buf.pending_setpoint = None; + buf.acknowledged = false; + } + } + + // Locked-out actuators (no CommandBuffer) still get shadow-exported. + for state in locked_actuators.iter() { + let _ = std::hint::black_box(state.actual_position); + exported += 4; + } + + counter.total_bytes += exported; + diag.record("ExportSystem", t0.elapsed()); +} + +// ───────────────────────────────────────────────────────────── +// System 5 — DiagnosticsSystem +// +// Runs last. Every 1 000 ticks prints a structured summary line, +// then resets the accumulation window. +// +// Takes ResMut only — no Res. +// Takes Res for the tick number. +// No Res/ResMut conflict on any resource. +// ───────────────────────────────────────────────────────────── + +fn rss_bytes() -> u64 { + #[cfg(target_os = "linux")] + { + if let Ok(status) = fs::read_to_string("/proc/self/status") { + for line in status.lines() { + if line.starts_with("VmRSS:") { + let kb: u64 = line + .split_whitespace() + .nth(1) + .and_then(|s| s.parse().ok()) + .unwrap_or(0); + return kb * 1024; + } + } + } + 0 + } + #[cfg(not(target_os = "linux"))] + { + // macOS: use proc_info via /proc is unavailable. + // Read vm_stat or just return 0 — the paper numbers come from Linux. + 0 + } +} + +fn diagnostics_system( + tick_res: Res, + mut diag: ResMut, + counter: Res, + profile: Res, + n_with_sensor: Query<(), With>, + n_all_processed: Query<(), With>, + n_alerts: Query<(), With>, + n_actuators: Query<(), With>, +) { + let t0 = Instant::now(); + let tick = tick_res.0; + + diag.ticks_in_window += 1; + + if tick % 1_000 != 0 || diag.ticks_in_window == 0 { + diag.record("DiagnosticsSystem", t0.elapsed()); + return; + } + + let n = diag.ticks_in_window as f64; + let rss = rss_bytes(); + + let mean = |name: &'static str| -> f64 { + diag.accumulated_elapsed_us + .get(name) + .copied() + .unwrap_or(0) as f64 + / n + }; + + let mean_tick_us = diag.total_tick_elapsed_us as f64 / n; + + println!( + "tick={tick:>7} | \ + entities={ne:>7} (sensor_active={sa:>6}, alerts={al:>4}, act={ac:>5}) | \ + faults={fi:>6} lockouts={lo:>3} | \ + rss={rss_mb:>6.1} MB | \ + tick_mean={tm:>7.1} µs | \ + ingest={ig:>6.1} sim={sm:>6.1} export={ex:>6.1} diag={dg:>5.1} µs | \ + exported={eb:.2} MB acks={acks}", + tick = tick, + ne = n_all_processed.iter().count(), + sa = n_with_sensor.iter().count(), + al = n_alerts.iter().count(), + ac = n_actuators.iter().count(), + fi = profile.total_faults_injected, + lo = profile.total_actuator_lockouts, + rss_mb = rss as f64 / 1_048_576.0, + tm = mean_tick_us, + ig = mean("IngestSystem"), + sm = mean("SimulationSystem"), + ex = mean("ExportSystem"), + dg = mean("DiagnosticsSystem"), + eb = counter.total_bytes as f64 / 1_048_576.0, + acks = counter.total_acks_processed, + ); + + // Reset window. + diag.accumulated_elapsed_us.clear(); + diag.ticks_in_window = 0; + diag.total_tick_elapsed_us = 0; + + diag.record("DiagnosticsSystem", t0.elapsed()); +} + +// ───────────────────────────────────────────────────────────── +// World setup — entity spawning +// ───────────────────────────────────────────────────────────── + +fn spawn_entities(world: &mut World, cfg: &Config) { + let mut rng = ChaCha8Rng::seed_from_u64(cfg.rng_seed ^ 0x1234); + + let n_sensor = (cfg.total_entities as f32 * 0.70).round() as usize; + let n_event = (cfg.total_entities as f32 * 0.20).round() as usize; + let n_actuator = cfg.total_entities - n_sensor - n_event; + + let sensor_units = [ + SensorUnit::Temperature, + SensorUnit::Vibration, + SensorUnit::Position, + ]; + + // ── Tier 1: SensorAsset ────────────────────────────────── + // spawn_batch keeps all components for the same archetype contiguous + // in memory — exactly the cache-coherent layout claimed in the paper. + world.spawn_batch((0..n_sensor).map(|i| { + let unit = sensor_units[i % 3]; + let rate = 10.0_f32 + (i % 90) as f32; + let offset = { + // Pull from rng in closure — ChaCha8Rng is not Send so we + // can't move it into par_iter; instead we precompute offsets + // sequentially here then use them in the batch. + // For the spawn batch we use a simple deterministic formula + // instead of rng to avoid borrow issues inside the closure. + ((i as f32 * 0.6180339887) % 1.0) - 0.5 + }; + ( + AssetId { id: i as u32, class: AssetClass::Sensor }, + SensorMetadata { + sample_rate_hz: rate, + unit, + calibration_offset: offset, + }, + RawSensorData { + value: offset, + timestamp_us: 0, + sequence_number: 0, + }, + ProcessedState { + filtered_value: offset, + last_updated_tick: 0, + }, + ) + })); + + // ── Tier 2: EventAsset ─────────────────────────────────── + let base = n_sensor; + // Staggered thresholds — prevents all entities from triggering at once. + world.spawn_batch((0..n_event).map(|i| { + let spread = (i % 20) as f32 * 0.05; + let upper = 0.6 + spread; + let lower = -0.6 - spread; + let consec = 2 + (i % 8) as u8; + let offset = ((i as f32 * 0.7320508) % 1.0) - 0.5; + ( + AssetId { id: (base + i) as u32, class: AssetClass::Event }, + SensorMetadata { + sample_rate_hz: 5.0 + (i % 10) as f32, + unit: SensorUnit::Temperature, + calibration_offset: offset * 0.1, + }, + RawSensorData { + value: 0.0, + timestamp_us: 0, + sequence_number: 0, + }, + ProcessedState { + filtered_value: 0.0, + last_updated_tick: 0, + }, + ThresholdConfig { + upper_bound: upper, + lower_bound: lower, + consecutive_violations_required: consec, + }, + ThresholdState { + current_violation_count: 0, + last_violation_tick: 0, + is_alerting: false, + }, + ) + })); + + // ── Tier 3: ActuatorAsset ──────────────────────────────── + let base = n_sensor + n_event; + // Use rng for actuator initial positions (small count, no closure issue). + let actuator_inits: Vec = (0..n_actuator) + .map(|_| rng.gen_range(-1.0_f32..1.0_f32)) + .collect(); + + world.spawn_batch((0..n_actuator).map(|i| { + let init_pos = actuator_inits[i]; + ( + AssetId { id: (base + i) as u32, class: AssetClass::Actuator }, + ActuatorState { + current_setpoint: init_pos, + actual_position: init_pos, + last_command_tick: 0, + }, + CommandBuffer { + pending_setpoint: None, + acknowledged: false, + }, + ActuatorHealth { + fault_code: None, + consecutive_failures: 0, + }, + // ProcessedState on actuators lets ExportSystem's ProcessedState + // query count all tiers unambiguously. + ProcessedState { + filtered_value: init_pos, + last_updated_tick: 0, + }, + ) + })); + + println!( + "[spawn] SensorAsset={n_sensor} EventAsset={n_event} \ + ActuatorAsset={n_actuator} total={}", + n_sensor + n_event + n_actuator + ); +} + +// ───────────────────────────────────────────────────────────── +// Schedule construction +// ───────────────────────────────────────────────────────────── + +fn build_schedule() -> Schedule { + let mut schedule = Schedule::default(); + + schedule.configure_sets(( + DtSystemSet::Fault, + DtSystemSet::Ingest, + DtSystemSet::Simulate, + DtSystemSet::Export, + DtSystemSet::Diagnostics, + ).chain()); + + schedule.add_systems(( + fault_injection_system.in_set(DtSystemSet::Fault), + ingest_system .in_set(DtSystemSet::Ingest), + simulation_system .in_set(DtSystemSet::Simulate), + export_system .in_set(DtSystemSet::Export), + diagnostics_system .in_set(DtSystemSet::Diagnostics), + )); + + schedule +} + +// ───────────────────────────────────────────────────────────── +// Main +// ───────────────────────────────────────────────────────────── + +fn main() { + let cfg = parse_args(); + + println!( + "ECS Digital Twin Benchmark — TRANSIT lab / UQAC\n\ + entities={} | ticks={} | warmup={} | fault_prob={:.1}%", + cfg.total_entities, + cfg.ticks, + cfg.warmup_ticks, + cfg.fault_probability * 100.0, + ); + + let mut world = World::new(); + let mut schedule = build_schedule(); + + // Insert all resources before spawning. + world.insert_resource(CurrentTick(0)); + world.insert_resource(FaultProfile::new(cfg.rng_seed, cfg.fault_probability)); + world.insert_resource(SimClock { current_us: 0, step_us: 1_000 }); + world.insert_resource(TickDiagnostics::default()); + world.insert_resource(ExportCounter::default()); + + spawn_entities(&mut world, &cfg); + + // ── Warmup ─────────────────────────────────────────────── + println!("[warmup] running {} ticks...", cfg.warmup_ticks); + for i in 0..cfg.warmup_ticks { + world.resource_mut::().0 = i; + world.resource_mut::().current_us += 1_000; + schedule.run(&mut world); + } + + // Reset all measurement state so warmup doesn't pollute results. + { + let mut diag = world.resource_mut::(); + diag.accumulated_elapsed_us.clear(); + diag.ticks_in_window = 0; + diag.total_tick_elapsed_us = 0; + } + { + let mut ctr = world.resource_mut::(); + ctr.total_bytes = 0; + ctr.total_acks_processed = 0; + ctr.total_alerts_seen = 0; + } + + println!("[warmup] done — starting measurement ({} ticks)", cfg.ticks); + + // ── Measurement loop ───────────────────────────────────── + let run_start = Instant::now(); + + for i in 0..cfg.ticks { + let tick_id = cfg.warmup_ticks + i; + + world.resource_mut::().0 = tick_id; + world.resource_mut::().current_us += 1_000; + + // Time the full tick including all five systems. + let tick_wall = Instant::now(); + schedule.run(&mut world); + let tick_elapsed = tick_wall.elapsed(); + + world.resource_mut::().total_tick_elapsed_us += + tick_elapsed.as_micros() as u64; + } + + let total_elapsed = run_start.elapsed(); + + // ── Final summary ───────────────────────────────────────── + let hz = cfg.ticks as f64 / total_elapsed.as_secs_f64(); + + // Borrow resources for final print. + let diag = world.resource::(); + let counter = world.resource::(); + let profile = world.resource::(); + + let n = diag.ticks_in_window.max(1) as f64; + let mean = |name: &'static str| -> f64 { + diag.accumulated_elapsed_us + .get(name) + .copied() + .unwrap_or(0) as f64 + / n + }; + let mean_tick_us = diag.total_tick_elapsed_us as f64 + / cfg.ticks as f64; + + println!( + "\n── Final Summary ──────────────────────────────────────\n\ + Total ticks (measurement): {ticks}\n\ + Total wall time: {wall:.3} s\n\ + Sustained tick rate: {hz:.1} Hz\n\ + Per-tick mean (wall): {ptw:.1} µs\n\ + \n\ + Per-system mean (µs):\n\ + IngestSystem: {ig:.1}\n\ + SimulationSystem: {sm:.1}\n\ + ExportSystem: {ex:.1}\n\ + DiagnosticsSystem: {dg:.1}\n\ + \n\ + Fault injection:\n\ + Total sensor faults: {fi}\n\ + Total actuator lockouts: {lo}\n\ + \n\ + Export totals:\n\ + Bytes exported: {eb:.2} MB\n\ + Acks processed: {acks}\n\ + Alerts seen: {alts}\n\ + \n\ + Memory (RSS at end): {rss:.1} MB\n\ + ────────────────────────────────────────────────────────", + ticks = cfg.ticks, + wall = total_elapsed.as_secs_f64(), + hz = hz, + ptw = mean_tick_us, + ig = mean("IngestSystem"), + sm = mean("SimulationSystem"), + ex = mean("ExportSystem"), + dg = mean("DiagnosticsSystem"), + fi = profile.total_faults_injected, + lo = profile.total_actuator_lockouts, + eb = counter.total_bytes as f64 / 1_048_576.0, + acks = counter.total_acks_processed, + alts = counter.total_alerts_seen, + rss = rss_bytes() as f64 / 1_048_576.0, + ); +} \ No newline at end of file