# Performance

> Performance Monitoring

- Skill: `vchelaru/performance-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/performance-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/performance-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/performance-2

---


# Performance Monitoring

`FlatRedBallService.Default.Performance` (a `PerformanceMonitor`, `src/Diagnostics/PerformanceMonitor.cs`) tracks rolling FPS, per-phase frame timing, and a per-collision-relationship severity breakdown over a 120-frame window.

**Off by default** — set `IsEnabled = true` before it records anything; until then every stat reads zero/empty.

```csharp
FlatRedBallService.Default.Performance.IsEnabled = true;

// later, e.g. every N frames from Screen.CustomActivity:
var perf = FlatRedBallService.Default.Performance;
Console.WriteLine(perf.GenerateReport());   // FPS + phase timing + collision severity, as a string
var fps = perf.Fps;                         // .Current / .Average / .Min / .Max
var worst = perf.GetCollisionReport();      // most expensive first; each row carries a PartitionStatus
```

`GenerateReport()` only builds a string — it does no I/O itself, so printing/logging/writing it is on the caller.

## Timer resolution (web)

`GenerateReport()` opens with a `Platform:` line and the measured clock step (`TimerResolutionMs`, probed once via `ProfileClock.MeasureResolutionMs`).

**Landmine:** browsers coarsen their clock as a Spectre mitigation — ~1ms on Firefox/Safari, ~0.1ms on Chrome — and `Stopwatch.Frequency` does not reflect it. Whole-pass totals stay accurate (start/end errors cancel), but any per-phase number smaller than one step reads as 0 or one full step and nothing between. The report warns automatically above 0.5ms.

Set `PlatformLabel` from the host — the engine targets `net10.0` and cannot read `navigator.userAgent` itself.

Every row in the collision report carries a `PartitionStatus`. `Unpartitioned` is the only value worth acting on, and the fix is to set the same `Factory<T>.PartitionAxis` on both sides of the relationship. `NotApplicable` means one side is not a factory, such as a `TileShapes`, a single entity, or a plain `List<T>`, so no axis setting would change it. See the `collision-relationships` skill.

See `src/Diagnostics/FrameProfile.cs` for the underlying per-frame timing struct.

