Initial commit

This commit is contained in:
Valère Plantevin
2026-04-20 21:23:37 -04:00
commit 27c05c982f
38 changed files with 2992 additions and 0 deletions

182
.gitignore vendored Normal file
View File

@@ -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

28
Cargo.toml Normal file
View File

@@ -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"]

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

8
results/final_table.csv Normal file
View File

@@ -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,
1 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
2 10000 5000 48.8 5.9 3.9 0.0 498016 0 5000000 104943
3 25000 5000 125.6 15.0 9.9 0.0 1243639 0 12500000 254654
4 50000 5000 254.0 30.5 19.9 0.0 2486121 0 25000000 511654
5 75000 5000 377.6 45.7 30.6 0.0 3730093 0 37500000 771425
6 100000 5000 504.2 61.8 41.0 0.0 4974449 0 50000000 1028407
7 150000 5000 754.2 92.5 61.7 0.0 7460636 0 75000000 1543036
8 200000 5000 1001.3 125.2 83.1 0.0 9946420 0 100000000 2056796

35
results/raw_10000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_100000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_150000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_200000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_25000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_50000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results/raw_75000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

271
results/summary.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

10
results/sysinfo.txt Normal file
View File

@@ -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

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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,
1 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
2 10000 5000 97.2 17.2 14.0 0.1 498016 0 5000000 104943
3 25000 5000 244.0 43.2 35.1 0.2 1243639 0 12500000 254654
4 50000 5000 496.0 94.5 70.8 0.2 2486121 0 25000000 511654
5 75000 5000 736.2 156.6 110.7 0.3 3730093 0 37500000 773395
6 100000 5000 985.7 217.2 150.1 0.2 4974449 0 50000000 1029861
7 150000 5000 1517.3 381.4 226.6 0.2 7460636 0 75000000 1538845
8 200000 5000 2022.7 582.0 300.7 0.2 9946420 0 100000000 2059136

35
results_rpi/raw_10000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

View File

@@ -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
────────────────────────────────────────────────────────

35
results_rpi/raw_25000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results_rpi/raw_50000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

35
results_rpi/raw_75000.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

283
results_rpi/summary.txt Normal file
View File

@@ -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
────────────────────────────────────────────────────────

22
results_rpi/sysinfo.txt Normal file
View File

@@ -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 ===

265
run_benchmark.sh Executable file
View File

@@ -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)"

935
src/main.rs Normal file
View File

@@ -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<String> = 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<f32>,
acknowledged: bool,
}
/// Written by SimulationSystem when actuator fails to converge.
#[derive(Component)]
struct ActuatorHealth {
fault_code: Option<u8>,
consecutive_failures: u8,
}
// ─────────────────────────────────────────────────────────────
// Resources
// ─────────────────────────────────────────────────────────────
/// Current tick counter — its own resource so systems can take
/// Res<CurrentTick> without conflicting with ResMut<TickDiagnostics>.
#[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<TickDiagnostics> and ResMut<TickDiagnostics>.
#[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<FaultProfile>,
active_sensors: Query<
Entity,
(With<RawSensorData>, With<SensorMetadata>),
>,
inactive_sensors: Query<
(Entity, &SensorMetadata),
(Without<RawSensorData>, With<ProcessedState>),
>,
actuators: Query<(Entity, &ActuatorHealth), With<CommandBuffer>>,
) {
let p = profile.fault_probability_per_tick;
for entity in active_sensors.iter() {
if profile.rng.gen::<f32>() < p {
commands.entity(entity).remove::<RawSensorData>();
profile.total_faults_injected += 1;
}
}
for (entity, meta) in inactive_sensors.iter() {
if profile.rng.gen::<f32>() < 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::<CommandBuffer>();
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<SimClock>,
mut profile: ResMut<FaultProfile>,
mut diag: ResMut<TickDiagnostics>,
) {
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::<f32>() < 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<CurrentTick> (not TickDiagnostics) to read the tick number,
// then ResMut<TickDiagnostics> only to record its elapsed time.
// No Res/ResMut conflict.
// ─────────────────────────────────────────────────────────────
fn simulation_system(
mut commands: Commands,
tick_res: Res<CurrentTick>,
// 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<TickDiagnostics>,
) {
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::<AlertEvent>();
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<CommandBuffer> and
// ResMut<CommandBuffer> 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<CommandBuffer>>,
mut counter: ResMut<ExportCounter>,
mut diag: ResMut<TickDiagnostics>,
) {
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<TickDiagnostics> only — no Res<TickDiagnostics>.
// Takes Res<CurrentTick> 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<CurrentTick>,
mut diag: ResMut<TickDiagnostics>,
counter: Res<ExportCounter>,
profile: Res<FaultProfile>,
n_with_sensor: Query<(), With<RawSensorData>>,
n_all_processed: Query<(), With<ProcessedState>>,
n_alerts: Query<(), With<AlertEvent>>,
n_actuators: Query<(), With<ActuatorState>>,
) {
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<f32> = (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::<CurrentTick>().0 = i;
world.resource_mut::<SimClock>().current_us += 1_000;
schedule.run(&mut world);
}
// Reset all measurement state so warmup doesn't pollute results.
{
let mut diag = world.resource_mut::<TickDiagnostics>();
diag.accumulated_elapsed_us.clear();
diag.ticks_in_window = 0;
diag.total_tick_elapsed_us = 0;
}
{
let mut ctr = world.resource_mut::<ExportCounter>();
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::<CurrentTick>().0 = tick_id;
world.resource_mut::<SimClock>().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::<TickDiagnostics>().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::<TickDiagnostics>();
let counter = world.resource::<ExportCounter>();
let profile = world.resource::<FaultProfile>();
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,
);
}