Product
Performance
On this page · 7 sections
A number without its machine is not a number. These are the measurements of node scripts/bench-agent.js on the machine below, run on 2026-09-22 with agent 1.18.0: the agent as a customer runs it, a simulator source of 2000 tags at 100 ms, a Modbus TCP emulator of 400 holding registers polled in 4 blocks every 50 ms, and 20 WebSocket clients subscribed to every simulator tag, for 10 s. Everything on one machine: the clients share the agent's CPU, so the figures are a lower bound on the agent and an upper bound on what one network carries.
The machine
| CPU | Intel(R) Core(TM) Ultra 7 255U, 14 logical cores |
| Memory | 31 GB |
| Platform | win32 10.0.26200 |
| Node.js | v24.18.0 |
Delivery
| Samples delivered a second, all clients | 366,927 |
| Per client a second | 18,346 of 20,000 produced (92 %) |
| Agent CPU while delivering | 0.3 cores |
| Agent resident memory | 125 MB with 2400 tags and 20 clients |
A share under 100 % means the agent's single thread could not serialise every sample for every client in time and the WebSocket buffers grew; the samples are not lost to the hub (the latest value is what a screen shows), but a screen sees fewer of them. The sizing rule that follows: tags × clients × updates a second ≈ 366,927 on a machine like this one; a station with 500 tags at 500 ms and 10 screens is at 10,000, well inside.
Modbus
| Poll requests a second | 68 of 80 asked (4 blocks of 100 registers every 50 ms) |
| Registers a second | 6,759 |
Against the emulator, on the same machine; a PLC on a network answers in its own time, and one request per block per poll is the point of the block reads.
A write, round trip
| Writes measured | 200 |
| Median | 0.6 ms |
| 95th percentile | 1 ms |
| 99th percentile | 17.1 ms |
From the client's write over the WebSocket to the confirmed sample back at the client, while the 20 clients were being fed. The simulator confirms at once; a PLC or an instrument adds its own answer time, and a poll-based source the rest of its interval.
The API and connections
| GET /api/tags with 2,400 tags | 12.9 ms median, 28.9 ms at the 95th percentile |
| 200 WebSocket clients connecting at once | 140 ms until all were open |
The designer and the app with a large project
Measured by node scripts/bench-designer.js on 2026-09-22 in Chromium (puppeteer), headless on the same machine: a project with a page of 300 components (tiles, gauges, LEDs, readouts, tanks, every one bound to a simulator tag), 20 further pages of 30, and a dataflow diagram of 300 nodes and 398 wires.
| The designer opens on the 300-component page | 1353 ms from navigation to the last cell (75 ms to the document) |
| Switching to a 30-component page | 507 ms |
| Switching back to the 300-component page | 1460 ms |
| Selecting a component (the inspector generated) | 23 ms |
| Drawing the 300-node diagram | 687 ms |
| The published app opens on the same page | 1841 ms, 24 MB of JavaScript heap |
An operator screen is rarely this dense; a page of 30 to 60 components is the usual one, and the figures scale with the count. The diagram's 300 nodes ran at their loop's period without falling behind.
Running it yourself
node scripts/bench-agent.js --tags 2000 --clients 20 --interval 100 --seconds 10 --doc node scripts/bench-designer.js --components 300 --pages 20 --nodes 300 --doc
The results go to studio/bench/agent.json; --doc rewrites this page from them. A station's real figure is the one measured on that station with its instruments: run the bench there before sizing.