Skip to main content
Version: Testnet (v4.1.0-rc.2)

Profiling Transactions

This guide shows you how to profile Aztec transactions to understand gate counts and identify optimization opportunities.

Prerequisites

  • aztec command installed (see installation)
  • Aztec contract compiled (aztec compile)
  • Basic understanding of proving and gate counts

Choosing a profiling tool

Aztec provides three ways to profile. Each serves a different purpose:

ToolWhat it measuresNeeds deployment?When to use
aztec profile gatesPer-function gate countsNoQuick check of individual function costs after compiling
aztec profile flamegraphPer-function flamegraph SVGNoDeep-dive into where gates come from inside a function
aztec-wallet profile / .profile() in aztec.jsFull transaction gate count including all kernel circuitsYesUnderstanding the true cost of a transaction end-to-end

In most cases, start with aztec profile gates for a quick overview, then use the full transaction profiling tools when you need to understand kernel overhead.

Quick profiling with aztec profile

These commands work on compiled artifacts directly — no deployment or running network required.

Gate counts

# Compile your contract
aztec compile

# Get gate counts for all functions
aztec profile gates ./target

Example output:

Gate counts:
────────────────────────────────────────────────────────────────────
my_contract-MyContract::constructor 5,200
my_contract-MyContract::my_function 14,832
my_contract-MyContract::transfer 31,559
────────────────────────────────────────────────────────────────────
Total: 3 circuit(s)

These are the gate counts for your contract functions alone, without kernel circuit overhead. See Understanding kernel overhead for how this translates to total transaction cost.

BB binary

aztec profile needs the Barretenberg (bb) backend binary. It is auto-detected from the @aztec/bb.js package. If auto-detection fails, set the BB environment variable:

BB=/path/to/bb aztec profile gates ./target

Flamegraphs

To generate an interactive flamegraph SVG for a specific function:

aztec profile flamegraph ./target/my_contract-MyContract.json my_function

This outputs a file like my_contract-MyContract-my_function-flamegraph.svg in the same directory. Open it in a browser for an interactive view where:

  • Width represents gate count
  • Height represents call stack depth
  • Wide sections indicate optimization targets
tip

If noir-profiler is not on your PATH, set the PROFILER_PATH environment variable:

PROFILER_PATH=/path/to/noir-profiler aztec profile flamegraph ./target/my_contract-MyContract.json my_function

Full transaction profiling

The tools above measure individual function gate counts. To understand the total proving cost of a transaction — including account entrypoints and kernel circuits — use aztec-wallet profile or .profile() in aztec.js. These require a deployed contract and a running network.

Profile with aztec-wallet

Use the profile command instead of send to get detailed gate counts:

# Import test accounts
aztec-wallet import-test-accounts

# Deploy your contract
aztec-wallet deploy MyContractArtifact \
--from accounts:test0 \
--args [CONSTRUCTOR_ARGS] \
-a mycontract

# Profile a function call
aztec-wallet profile my_function \
-ca mycontract \
--args [FUNCTION_ARGS] \
-f accounts:test0

Reading the output

The profile command outputs a per-circuit breakdown:

Per circuit breakdown:

Function name Time Gates Subtotal
--------------------------------------------------------------------------------
- SchnorrAccount:entrypoint 12.34ms 21,724 21,724
- private_kernel_init 23.45ms 45,351 67,075
- MyContract:my_function 15.67ms 31,559 98,634
- private_kernel_inner 34.56ms 78,452 177,086

Total gates: 177,086 (Biggest circuit: private_kernel_inner -> 78,452)

Key metrics:

  • Gates: Circuit complexity for each step
  • Subtotal: Accumulated gate count
  • Time: Execution time per circuit

Notice that the kernel circuits (private_kernel_init, private_kernel_inner) appear alongside your contract functions. These are protocol overhead — see Understanding kernel overhead.

Profile with aztec.js

const result = await contract.methods.my_function(args).profile({
from: walletAddress,
profileMode: "full",
skipProofGeneration: true,
});

// Access gate counts from execution steps
for (const step of result.executionSteps) {
console.log(`${step.functionName}: ${step.gateCount} gates`);
}

// Access timing information
console.log("Total time:", result.stats.timings.total, "ms");

Profile modes

  • gates: Gate counts per circuit
  • execution-steps: Detailed execution trace with bytecode and witnesses
  • full: Complete profiling information (gates + execution steps)

Set skipProofGeneration: true for faster iteration when you only need gate counts.

Generate flamegraphs with noir-profiler

For deeper analysis of individual contract functions beyond what aztec profile flamegraph provides, you can use the Noir profiler directly. The profiler is installed automatically with Nargo.

# Compile your contract first
aztec compile

# Generate a gates flamegraph (requires bb backend)
noir-profiler gates \
--artifact-path ./target/my_contract-MyContract.json \
--backend-path bb \
--output ./target

# Generate an ACIR opcodes flamegraph
noir-profiler opcodes \
--artifact-path ./target/my_contract-MyContract.json \
--output ./target

For detailed usage, see the Noir profiler documentation.

Understanding kernel overhead

When you profile a full transaction, you'll see kernel circuits alongside your contract functions. These are protocol overhead — the private kernel runs once per private function call in the transaction. Even a typical transaction calling a single contract function involves two private calls (the account entrypoint + your function), totaling ~427k gates of which only ~14k are your function.

For a detailed breakdown of kernel phases and their gate costs, see Private Kernel Circuit - Performance Impact.

Gate count guidelines

These are rough guidelines for a single contract function's gate count (i.e. what aztec profile gates reports). A typical transaction (e.g. a token transfer) totals ~500,000 gates across all circuits including kernel overhead, so use that as a reference point.

Gate CountAssessment
< 50,000Excellent
50,000 - 200,000Good
200,000 - 500,000Consider optimizing
> 500,000Worth optimizing if possible

Note that a high gate count does not prevent transaction inclusion — it only affects client-side proving time. See Private Kernel Circuit - Performance Impact for details.

Next steps