Demystifying MiniProfiler Timing: Request Duration vs. Action Duration

MiniProfiler is a fantastic, lightweight profiling tool for ASP.NET and ASP.NET Core applications. However, interpreting its hierarchical execution timing table can occasionally be confusing—especially when differentiating between the root request URL and the controller action execution.

If you've noticed entries like https://localhost:... and Action: Controller.ActionMethod reporting different numbers under duration (ms) and with children (ms), this guide breaks down exactly what those metrics mean and how the ASP.NET Core request pipeline influences them.

The Anatomy of MiniProfiler Columns

To understand the breakdown, let's look at the core metrics MiniProfiler reports:

  • duration (ms) (Self Time): The time spent executing code exclusively inside this step, excluding the execution time of any child steps nested beneath it.
  • with children (ms) (Total Time): The aggregate wall-clock time spent within this step plus all nested child steps beneath it.
  • from start (ms): The relative offset indicating when this step began since the overall profiling session started.

Breaking Down Your Specific Example

Consider the timing breakdown from the question:

| Step                                                     | duration (ms) | with children (ms) | from start (ms) |\n|----------------------------------------------------------|--------------:|-------------------:|----------------:|\n| https://localhost:7105/Admin/Import                      |          0.11 |            1277.58 |           +0.00 |\n| Action: HardwareStore.Controllers.AdminController.Import |         12.18 |            1277.47 |           +0.08 |\n| reader.Read()                                            |        882.14 |             882.14 |          +12.23 |\n| writer.Write                                             |        383.02 |             383.02 |         +894.38 |

1. What does the 0.11 ms on the URL line represent?

The top row (https://localhost:7105/Admin/Import) represents the overall HTTP request as intercepted by MiniProfiler middleware. Its with children value of 1277.58 ms is the complete life cycle of the request.

The duration (ms) of 0.11 ms is the self-time of the middleware outside of the MVC pipeline execution. This includes early routing, basic middleware overhead, and closing the response stream after the action has completely finished.

2. What does the 12.18 ms on the Action line represent?

The second row represents the MVC Action invocation. Notice that its total time (with children) is 1277.47 ms, but its isolated self-time (duration) is 12.18 ms.

That 12.18 ms represents the framework overhead inside the action filter pipeline and controller invocation that is not accounted for by your explicit child steps (reader.Read() and writer.Write). This includes:

  • Model Binding and Validation: Processing the [FromForm] IFormFile file upload.
  • Controller Instantiation: Resolving dependencies via Dependency Injection (DI).
  • Action Filter Execution: Running authorization, resource filters, or action filters.
  • Returning the Result: The execution of return Ok(); and the overhead of entering/exiting the using statements.

Mathematically, it adds up perfectly:

Action Total (1277.47 ms) - reader.Read() (882.14 ms) - writer.Write (383.02 ms) - other step overhead ≈ 12.18 ms

3. Why do reader.Read() and writer.Write have identical "duration" and "with children" values?

Yes, this is completely normal! When a step has no child steps wrapped inside it (i.e., no nested MiniProfiler.Current.Step(...) calls), its total time is strictly equal to its self time. Because reader.Read() is a leaf node in the profiling tree, its isolated duration and its time "with children" are identical (882.14 ms).

Key Takeaways for Performance Tuning

  • Look at "with children" for bottlenecks: To find where the user-facing latency is coming from, follow the largest numbers in the with children column down the tree. In your case, reader.Read() accounts for nearly 70% of the total request time.
  • Watch for high Action self-duration: If your Action:... self-time spikes significantly (e.g., several hundred milliseconds), look into costly model binding, massive payloads being deserialized, or heavy work performed inside Action Filters or custom model binders.