skip to content

How do you use `--profile` to determine whether your build's bottleneck is the configuration phase or the execution phase?

level: middleimportance: must knowfreq 60%

answer

  1. --profile -> build/reports/profile/*.html
  2. sections: Configuration vs Task Execution
  3. profile `help` to isolate configuration
  4. task table shows EXECUTED/UP-TO-DATE/FROM-CACHE
  5. offline, no service needed

basics

~10 s

Run the build with --profile. Gradle writes an HTML report to build/reports/profile/ that breaks time into Configuration and Task Execution sections, plus per-project and per-task timings, so you can see which phase dominates.

solid answer

~40 s

Add `--profile` to any build (`gradle build --profile`). Gradle records timings and writes `build/reports/profile/profile-<timestamp>.html`. The report has tabs/sections for **Summary** (total wall time), **Configuration** (time spent configuring each project), **Dependency Resolution**, and **Task Execution** (per-task durations with their state — executed, up-to-date, from-cache). To diagnose, compare the Configuration total against the Task Execution total. A large Configuration slice — especially relative to a near-empty execution slice when you run a cheap task like `help` — means the bottleneck is the configuration phase. A small Configuration slice with a few dominating tasks means execution is the bottleneck, and you'd then look at caching, incremental builds, or parallelism. Profiling a trivial task (`gradle help --profile`) isolates configuration cost cleanly because execution is near zero.

code

bash · 5 lines
bash
# Diagnose: is it configuration or execution?
gradle build --profile          # full build report
gradle help  --profile          # near no-op -> isolates configuration cost
# Open the newest file in build/reports/profile/ and compare
# the 'Configuration' total against the 'Task Execution' total.

go deeper

for a junior

Know that --profile exists and produces an HTML report under build/reports/profile.

for a middle

Read the Configuration vs Task Execution split and use a help profile to isolate configuration.

for a senior

Combine profile with outcome labels and dependency-resolution timing to localize the cause, and know when to escalate to scans.

for a principal

Standardize profiling/scan capture in CI and developer tooling so phase regressions are caught fleet-wide, not ad hoc.

## What `--profile` produces The `--profile` flag instruments the build and emits a standalone HTML report at `build/reports/profile/profile-<yyyy-MM-dd-HH-mm-ss>.html`. It needs no external service (unlike build scans) and works offline, which makes it the first-reach tool for local diagnosis. ## Reading the report — section by section The report opens on a **Summary** with the total build wall-clock time and a breakdown of the major buckets: - **Configuration** — how long each project took to *configure* (evaluate its build script and realize/configure tasks). This is your configuration-phase signal. - **Dependency Resolution** — time spent resolving configurations; if this is large it usually inflates configuration time (resolving at configuration time is a classic anti-pattern). - **Task Execution** — a per-task table with each task's duration and its outcome label (`EXECUTED`, `UP-TO-DATE`, `FROM-CACHE`, `NO-SOURCE`). This is your execution-phase signal. ## The diagnostic procedure 1. **Profile the real command** you care about: `gradle build --profile`. Note the Configuration total vs the Task Execution total. 2. **Profile a no-op**: `gradle help --profile`. Because `help` executes essentially nothing, almost the entire time here is configuration. If `help` is already slow, configuration is your problem and you don't even need to look further. 3. **Compare**. If Configuration ≈ the slow part, the fix lives in script/plugin/task-creation cost. If Task Execution dominates with a few heavy tasks, the fix lives in caching/incremental/parallel execution. ## Interpreting outcomes in the task table The outcome column tells you whether execution time is even avoidable: a task marked `EXECUTED` did real work; `UP-TO-DATE` and `FROM-CACHE` took ~no time. If your slow tasks already show `FROM-CACHE`, execution isn't your issue. If they show `EXECUTED` every run despite unchanged inputs, you have a separate up-to-date/caching problem worth chasing. ## Limitations `--profile` shows *where* time went but not always *why* (it won't point at the specific eager `tasks.create` call). Build scans go deeper with a timeline and configuration insights; reach for `--scan` when the profile says "configuration is slow" but you need to know which plugin or script. The actual lazy-API remedy (configuration avoidance) is its own topic. ```text build/reports/profile/profile-2026-06-30-10-15-02.html Summary total: 41.3s Configuration 28.9s <-- bottleneck here Dependency Res. 6.1s Task Execution 12.4s ```

  • Where exactly is the profile report written?
    To `build/reports/profile/profile-<timestamp>.html` in the build's root project. A new timestamped file is created each run, so old reports are preserved.
  • Why profile `gradle help` specifically?
    Because `help` performs almost no execution work, virtually all of its time is configuration. A slow `help` profile is a clean, unambiguous signal that the configuration phase is the bottleneck.
  • What does a `FROM-CACHE` outcome in the task table tell you?
    That the task's outputs were reused from the build cache rather than recomputed, so that task contributed almost nothing to execution time — look elsewhere for the slow part.

saying these in an interview costs you the question

  • Saying `--profile` uploads data to a server — it doesn't; that's `--scan`.
  • Reading only the total time and not the per-phase split, so you can't tell which phase to fix.
  • Assuming a single profile of a fully-cached build is representative — profile a clean/changed build too.

context