---
title: "Profiling flow performance"
url: "https://trailassembly.com/flow-performance-profiling/"
topic: "Flow and Automation"
published: "2026-09-17T08:13:01+00:00"
updated: "2026-10-06T13:57:13+00:00"
---

# Profiling flow performance

**The Flow Builder debug panel tells you what ran, not how long it took.** For timing you need a debug log with the Workflow category set to FINER or FINEST, which produces FLOW_ELEMENT_BEGIN and FLOW_ELEMENT_END lines with nanosecond timestamps. Subtract one from the other and you have per-element timing. Then profile with 200 records, because a one-record run tells you nothing about the shape of the problem.

## Setting up the log

Setup, Debug Logs, add a trace flag on the user who will run the flow. Create a debug level with Workflow at FINER and Apex Code at DEBUG. FINEST on Workflow adds variable assignment detail and makes the log much larger, so start at FINER and go further only when you need to see values.

## The lines that matter

Open the log and filter for FLOW. You are looking for:

- **FLOW_START_INTERVIEW** and the corresponding finish line, bracketing the whole interview.

- **FLOW_ELEMENT_BEGIN** and **FLOW_ELEMENT_END**, one pair per element execution, naming the element and its type.

- **FLOW_BULK_ELEMENT_BEGIN**, **FLOW_BULK_ELEMENT_END** and **FLOW_BULK_ELEMENT_DETAIL**, which is where record-triggered flows processing a batch show up, with the record count and elapsed time.

- **FLOW_VALUE_ASSIGNMENT** at FINEST, showing what each variable was set to.

Each line begins with a wall-clock time and a nanosecond offset in brackets. The offset is the number to use. Take the FLOW_ELEMENT_END offset, subtract the matching FLOW_ELEMENT_BEGIN offset, and divide by a million for milliseconds. Tedious by hand, trivial with a script over the raw log, and worth automating once if you do this regularly.

At the bottom of the log, CUMULATIVE_LIMIT_USAGE gives the totals for the transaction: SOQL queries, query rows, DML statements, DML rows, heap, and CPU time. That is where you find out whether you are near a ceiling.

## Reading it: what is usually slow

The distribution is predictable. In rough order of how often they are the answer:

**A [Get Records](https://trailassembly.com/flow-get-records-behaviour/) inside a Loop.** One query per iteration. This is the single most common cause and it is obvious in the log, because you see the same element name over and over. Query once before the loop into a collection and look values up from there.

**Automatically store all fields on a wide object.** The query selects every accessible field for every row. On an object with hundreds of fields and a few thousand rows, that is a heap problem and a query time problem at once. Switch to choosing fields.

**Loop element count.** Every element inside a loop is executed once per iteration against the 2,000 executed elements budget, and each one costs CPU. A loop with twelve elements over two hundred records is 2,400 executions and the interview fails.

**Formulas evaluated inside loops.** A formula recalculates on every reference. If its inputs do not change per iteration, assign it to a variable before the loop and reference the variable.

## Profile in bulk or do not bother

Every serious flow performance problem is a bulk problem. Load 200 records with Data Loader while the trace flag is active and read that log. The per-record cost you measured on one record is not the cost at 200, because queries return more rows, collections get larger, and loops iterate more.

The failure you are trying to avoid is “Apex [CPU time limit](https://trailassembly.com/cpu-time-limit/) exceeded”: 10 seconds synchronous, 60 asynchronous. A flow can sit comfortably inside that at one record and fail at eighty.

## The structural fixes

Before micro-optimising elements, check the three things that change the order of magnitude.

**Use a [before-save flow](https://trailassembly.com/before-save-vs-after-save/) for same-record field updates.** Setting fields on the triggering record in a before-save flow skips the second save entirely, and the difference against an after-save flow doing the same work is large.

**Tighten entry criteria.** Setting the trigger to run only when a record is updated to meet the conditions stops the flow firing on every save. A flow that does nothing still costs the transaction something to start and evaluate.

**Move work to an asynchronous path.** Anything the user does not need to see completed can run after the save, in its own transaction, with its own limits and a longer CPU allowance.

## Frequently asked

**Does the debug panel show timings?** No. It shows order, values, and outcomes. Timing only comes from the debug log.

**Is a Decision with many outcomes slow?** No. Decisions, Assignments and formulas are cheap compared with anything that touches the database. Spend your attention on queries and loops.

**How do I profile a [screen flow](https://trailassembly.com/screen-flow-validation/)‘s render time?** The debug log covers the server side only. For the browser side, use the Lightning component debug mode and the browser’s network timing, and reduce the number of components and queries running per screen.

---

Source: https://trailassembly.com/flow-performance-profiling/
Trail Assembly — independent Salesforce write-ups.
