LLM-Assisted Performance Optimization: Profiling and Iteration
Performance optimization has a repeatable inner loop: identify the work on a slow path, change its cost, and measure the result while preserving behavior. Coding models can participate throughout that loop. They can follow execution paths, inspect profiles, construct benchmarks, and implement candidate changes, which makes a well-defined performance objective a useful engineering task for an agent.
The leverage comes from feedback. A trace identifies where time goes; a correctness check constrains the implementation; a benchmark determines whether the candidate improved the path. Each result gives the next iteration more information than the original description of the problem.
Anthropic’s account of its claude.ai performance work describes this approach across user journeys and local benchmarks.1 The method applies more broadly because the essential components are ordinary profiling, experimentation, and software review.
The optimization objective
A useful objective begins and ends with observable application behavior. Opening a project may begin with a click and end when its records are rendered and search accepts input. Sending a message may end when the interface displays the submitted message and is ready for the next action.
These boundaries determine which work is part of the problem. A spinner marks progress, but it does not establish that the requested content is usable. Receiving a response establishes network completion, while parsing and rendering may still be on the critical path.
A concrete task definition can be expressed as:
Journey: open a project containing 10,000 records.
Start: the user activates its link.
End: visible records are rendered and search accepts input.
Behavior: preserve ordering, selection, navigation, and result counts.
Objective: reduce p95 duration within a defined memory budget.
Evidence: profiles, raw timing samples, checks, and the implementation diff.
The fixture and behavior define the work being optimized. Keeping them stable allows a sequence of candidate implementations to be compared against the same task. A correction to the benchmark itself is a separate change, with its own explanation.
The profile connects a symptom to a mechanism
Elapsed time contains both execution and waiting. CPU sampling identifies active functions. A timeline reveals sequential requests, scheduling gaps, and blocked work. Resource measurements show whether the execution is limited by CPU, memory, storage, or another service.
Brendan Gregg’s USE method organizes resource investigation around utilization, saturation, and errors.2 The distinction is useful here: an idle CPU waiting on storage presents a different optimization problem from a saturated CPU repeatedly parsing the same input.
Suppose a 500 ms operation spends 300 ms parsing, 150 ms waiting for data, and 50 ms rendering. Making parsing twice as fast reduces completion time to 350 ms, a 1.43x speedup. Eliminating all rendering work would save at most 50 ms. The phase breakdown provides an upper bound on the value of each candidate.
A measured mechanism then gives the agent a tractable task. Building an index once can replace repeated scans. Reusing a parsed representation can remove repeated decoding. Overlapping independent requests can reduce serial wait. Each proposal identifies both the work removed and the condition under which that saving should appear.
The experiment loop
reproduce the path
-> profile its execution
-> propose a change tied to the profile
-> run behavioral checks
-> repeat the benchmark
-> inspect the result and implementation
-> retain or revert the candidate
Small changes make this loop informative. When one candidate changes parsing, caching, and scheduling together, a gain or regression is harder to attribute. A sequence of focused changes produces an explanation for each improvement and makes later regressions easier to locate.
A useful experiment record includes failed candidates as well as successful ones:
| Candidate | Mechanism | Behavioral condition | Performance question |
|---|---|---|---|
| Cached index | Avoid repeated scans | Result IDs and order remain correct | Does lookup dominate enough to matter? |
| Deferred parser | Move work off the opening path | Content is ready before interaction | Does the next action inherit the delay? |
| Larger batch | Amortize fixed overhead | Cancellation and errors remain correct | How does the tail change? |
The record preserves information about the workload. A rejected candidate may become useful at another scale, while its failure explains why it was unsuitable for the current path.
Measuring a browser journey
Browser User Timing marks provide application-defined start and end events.3 They make it possible to measure a particular interaction rather than treating all page activity as one load metric.
The end event depends on the result. Data arrival, DOM update, layout, and paint are distinct stages. An input can appear before its event handling is ready, and a framework can finish a render before the browser displays the frame. The timing boundary should correspond to the application behavior being studied.
Browser traces complement the interval with an explanation. Long tasks identify stretches where input handling competes with other main-thread work. Layout and style recalculation show how a small data update can trigger a large amount of rendering work. Network timing identifies resources that are discovered late or requested serially.
The same principle applies outside a browser. A CLI’s start-to-exit duration includes process and module initialization. A database request includes query execution and result transfer. A model request includes queueing and output generation. The boundary follows the task.
Stable proxies for a fast inner loop
Wall-clock measurements include variation from scheduling, caches, and shared resources. Counts of instructions, allocations, queries, or rendering operations can provide more stable feedback for a narrow path.
Anthropic’s article describes using local metrics and checking that improvements in those metrics translated into elapsed-time gains.1 The connection is what makes a proxy useful. Fewer instructions may still leave a memory bottleneck unchanged. Fewer queries may transfer more data. Fewer DOM mutations may require a larger replacement operation.
A proxy therefore has two roles. It gives the agent a low-noise measure while exploring, and it can become a regression check after its relationship to the user journey is established. Wall-clock and field measurements remain the connection back to the application.
Experimental variation
Warmup, caching, thermal behavior, and background load affect the comparison. Alternating baseline and candidate runs reduces the chance that a changing environment consistently favors one version. A steady-state benchmark warms the relevant JIT and caches; a cold-start benchmark measures those costs explicitly.
Individual samples preserve the shape of the result. A median describes typical behavior, while the tail exposes occasional expensive paths. A p95 estimated from twenty runs depends on roughly the slowest one or two observations, so its stability is different from a percentile estimated from thousands of requests.
Input variation is equally important. A parser improvement can depend on text encoding. A lookup improvement can depend on collection size. A cache can perform well until updates invalidate it. A second fixture with different size or structure shows whether the mechanism extends beyond the case used during optimization.
For UI changes, behavior includes keyboard navigation, selection, scroll position, and the next interaction. Deferring work can improve opening time while moving the delay to the first keystroke. Measuring the adjacent action reveals that transfer.
Different workloads expose different levers
| Workload | Useful objective | Common sources of cost |
|---|---|---|
| Web application | Time to usable input, navigation latency | Initialization, rendering, resource discovery |
| CLI | Cold start and task completion | Module loading, subprocesses, repeated parsing |
| Database service | Tail latency at a fixed arrival rate | Query plans, round trips, serialization |
| Model server | Goodput under latency limits | Batching, KV storage, prefill scheduling |
| Data pipeline | Completion time and records per second | I/O, allocation, compression, skew |
The objective leaves room for the implementation to change. A request to reduce opening time can lead to an algorithmic improvement, a cache, or a better schedule. Prescribing memoization in advance narrows the search before the profile has identified the expensive work.
Correctness checks define the allowable search space. Exact CLI output, query results, model-quality criteria, and browser interactions each establish what the faster implementation must continue to do.
Load and tail latency
An isolated request measures service cost under little contention. A deployed service also has queues. Reducing service time can improve both the request itself and the waiting time of subsequent requests. Shared caches, larger batches, and additional concurrency can change that relationship.
The Tail at Scale describes why occasional delays become significant in large services.4 A request that depends on many components can inherit the slowest of them, making tail behavior a system-level property.
Arrival-rate experiments expose this effect. Error rates and rejected requests remain part of the result, since dropping slow work can otherwise look like a latency improvement. For interactive software, slower devices reveal contention that may be almost invisible on a development machine.
From a local result to a maintained improvement
A release connects the experiment to field behavior. Comparing the same journey by build, device class, and cache state shows where the gain appears. A mismatch between local and field results identifies a difference in workload or critical path that the next experiment can address.
Once a metric is stable and correlated with the application, it can protect the improvement in CI. The threshold represents an observed property of the implementation, with enough tolerance for the measurement method. The profile and experiment record explain the reason for the threshold.
The agent’s contribution is the speed of the loop: more candidate explanations can be investigated, implemented, and measured. The quality of the outcome comes from the objective and the feedback. Together they turn a vague report of slowness into a set of specific, maintainable changes.
References
Footnotes
-
Raymond Wang, Sam Attard, and Issac G., How we made claude.ai 3x faster in two weeks, September 23, 2026. Added in the September 25 revision. ↩ ↩2
-
Brendan Gregg, The USE Method. Resource utilization, saturation, and errors. ↩
-
MDN, Performance.mark(). Application-defined timing markers. ↩
-
Jeffrey Dean and Luiz André Barroso, The Tail at Scale, Communications of the ACM, 2013. Tail latency in large services. ↩
A useful tuning loop distinguishes a configuration change from a different experiment.
- Baseline
Freeze the model revision, prompt set, output length, and quality checks.
- Profile
Separate loading, prefill, decode, and scheduling costs.
- Candidate
Change the setting that addresses the observed bottleneck.
- Accept or revert
Compare repeated runs and retain the change only if it meets the objective.
A faster run is evidence only when the workload and correctness conditions remain comparable.
These changes can matter, but they must be named as changes to the experiment.
| Case | Apparent improvement | Confounding change |
|---|---|---|
| Lower first-token time | A prefix was reused | Compare cache state explicitly |
| Higher tokens per second | The output became shorter or easier | Hold the token budget and workload fixed |
| Lower memory use | Precision or context was reduced | Repeat quality and target-context validation |
| Better aggregate throughput | Concurrency increased | Check per-request latency and deadlines |
Report the configuration and workload with the result, rather than attributing every gain to the last parameter edited.