An OCS2 DDP timing percentage describes a share of its instrumented solver stages. In the current GaussNewtonDDP implementation, the denominator is the sum of six timer totals. Use that breakdown to choose a solver phase to investigate, then use a CPU or memory profiler to ask a more specific question about its implementation.
A phase’s percentage can fall even while that phase takes longer. Reading the absolute duration beside the share prevents a seemingly improved profile from hiding extra work.
The percentage comes from six accumulated totals
The OCS2 profiling guide describes the built-in timing summary, enabled through displayShortSummary in the DDP settings. It can also be read through getBenchmarkingInfo(). The guide’s printed-summary route uses the solver destructor, so it is useful to distinguish that accumulated report from an individual control-cycle trace.
The current GaussNewtonDDP source adds the timer totals for initialization, LQ approximation, backward pass, controller computation, search strategy and dual-solution work. Each displayed share divides its phase total by that sum. Each row separately prints an average from the corresponding timer.
The distinction between totals and averages matters. The initialization timer surrounds initial trajectory preparation, while LQ approximation and the backward pass are timed inside the optimization loop. Read the percentage from accumulated totals; the iteration count printed above the table is not a reason to assume that every timer has the same number of samples.
These phase labels narrow the investigation; a large share still leaves the functions inside that phase to inspect.
A smaller share can accompany more computation
Consider these hypothetical accumulated measurements for comparable problem sets with the same number of measured LQ approximation calls:
| Measurement | Run A | Run B |
|---|---|---|
| LQ approximation total | 20 ms | 30 ms |
| Sum of all six timer totals | 100 ms | 200 ms |
| LQ approximation share | 20% | 15% |
The LQ share drops by five percentage points, but its accumulated duration rises by 50%. Other timed work grew enough to make LQ approximation a smaller part of a larger total. If the engineering goal was to reduce approximation time, the percentage alone points in the wrong direction.
The same arithmetic works in reverse. Reducing search-strategy work can make an unchanged backward pass occupy a larger percentage. That larger slice can be a consequence of improvement elsewhere, rather than a new regression in the backward pass.
Compare like-for-like problem sets before interpreting these changes: horizon, task, solver configuration and stopping conditions can change the work performed. When iteration counts differ, retain the counts and absolute totals rather than collapsing the comparison into one percentage.
Choose the profiler that answers the next question
The built-in report locates work by solver phase. The tools in the official guide expose different kinds of evidence:
| Question after reading the DDP report | Tool or output to inspect |
|---|---|
| Which functions account for sampled CPU activity? | perf record and its call-graph report |
| Did instruction, cache-miss or context-switch counters change? | perf stat |
| Where does heap use grow during the run? | Massif memory snapshots |
| Which code is associated with cache and branch behavior? | Cachegrind output |
For example, a large LQ share is a starting point for a function-level profile. A call-graph sample can direct attention toward model evaluation or derivative code. Counter changes can then help distinguish extra instructions from changed cache behavior. A heap profile answers a different question: which allocations account for the memory use. These observations become useful together when they refer to the same workload and the question remains explicit.
The guide treats perf results as statistical measurements and recommends keeping build and CPU operating conditions controlled. It also lists frame-pointer or debug-information settings for the relevant tools. Record those conditions with the result; a debug build and an optimized build can move the hotspot as well as change its duration.
Keep the timing window separate from the robot’s response time
The six-timer denominator is computed inside the solver. An application can also spend time acquiring a state estimate, passing data to the solver or dispatching the resulting control. Those operations need their own observation points. The robot inference latency guide explains how to follow a physical observation through to command application when that wider interval is the question.
There is another concrete boundary in the OCS2 code: reset() clears the benchmarking timers, but it also clears stored primal and dual solution data. Calling it solely to restart a timing window changes more than the report. Choose a comparison window with the solver’s initialization state in mind, and distinguish a newly initialized run from continued operation.
Start with the phase whose absolute work changed, use its share to understand the surrounding workload, and choose the profiler that can expose the suspected cause. That sequence produces a specific next investigation without confusing a solver-stage chart with a complete robot timing trace.
This article reads the OCS2 guide and implementation checked on October 16, 2026. The table uses invented measurements; no solver, CPU profiler or memory profiler was executed.