CPU: measure code and remove unnecessary work
Separate function cost from waiting, distinguish C# from native profiling and verify the result.
Choose the right instrument
In the current 1.0.0 source, CSharpLanguage's profiling-start methods are empty and function-statistics methods return 0. Built-in script profiling is therefore not an established way to see every C# method's duration. Engine graphs and custom monitors use separate data sources.
| Question | Instrument | Limit |
|---|---|---|
| Duration of a particular synchronous C# section | System.Diagnostics.Stopwatch around the section | Elapsed time includes preemption and waits |
| Frame intervals and occasional long frames | FrameTimingProbe from the previous guide | Does not show the causal stack |
| Native calls, C# stacks, waits, GC | A profiler supporting the process, runtime and build symbols | Verify tool support first; a step-by-step integration is not established here |
| Object count or completed-operation duration | A custom monitor | Its callback runs when queried, not automatically for every event |
Performance.GetMonitor(TimeProcess) and TimePhysicsProcess return seconds. In this build, the main loop publishes the accumulated maximum of the corresponding time roughly once per second, then starts a new accumulation interval. This is neither a current _Process value, a CPU percentage nor the sum of all C# method durations. Multiply by 1000 only to display milliseconds.
Reduce measured work
- Fix the input, operation count and correct result. Warm C# code when comparing normal post-start execution.
- Time only the required synchronous operation. Log and format the result afterward. Repeat a matching series.
- Check the algorithm: scanning every pair scales differently from indexed lookup. Cache only with a clear invalidation rule and memory bound.
- Remove repeated per-frame Entity/resource searches when a reference is already known and its lifetime is controlled. Do not replace them with a permanent cache of an object that may be deleted.
- Measure temporary collections, strings and other allocations separately. Frequent collections can cause stalls, but allocated bytes are not frame duration. Array reuse requires ownership and correct reset.
- Compare frame intervals and gameplay results again in the same build on the same device.
Inclusive function time and self time excluding child calls are different measurements. Adding a parent's duration to all child durations double-counts work. Waiting for another thread or lock also does not prove that the current thread was performing useful CPU work.
When a worker helps
A worker is appropriate when work is substantial, independent of the active tree and allowed to complete later. The complete example counts primes in a .NET task and a WorkerThreadPool group. The known count through 100000 is 9592; the check establishes correctness, not a speedup.
The demo publishes elapsed time from scheduling to main-thread result consumption. It includes queueing, computation and waiting for the next _Process. Do not label it pure function time. Splitting a tiny operation into many tasks may cost more than it saves.
If only 20% of the original time can be accelerated, making that part instantaneous still leaves 80%: maximum overall speedup is 1 / 0.8 = 1.25. Establish the work share first. More cores do not automatically fix indefinite waits, unnecessary loading or a poor algorithm.