Skip to main content

Gas Profiling

Understanding gas consumption helps optimize your Move code and estimate transaction costs. The Move testing framework provides built-in tools to measure gas usage during test execution. In addition to that, a special utility sui analyze-trace is available for more thorough analysis of gas usage.

The statistics shown by -s only reflect computation units - they do not include storage costs. Additionally, compiler computation units don't map directly to actual on-chain gas charges; they show relative computational complexity, useful for comparing implementations against each other. To get actual gas costs, publish your package to testnet and measure real transactions.

Simple Measurement: Test Statistics

Use the -s or --statistics flag with sui move test to see execution time and gas consumption for each test:

sui move test -s

The output shows a table with three columns:

Test Statistics:

┌────────────────────────────────────────────────────────┬────────────┬───────────────────────────┐
│ Test Name │ Time │ Gas Used │
├────────────────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::my_module::test_simple_operation │ 0.003 │ 1 │
├────────────────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::my_module::test_complex_operation │ 0.011 │ 59 │
├────────────────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::my_module::test_with_objects │ 0.008 │ 25 │
└────────────────────────────────────────────────────────┴────────────┴───────────────────────────┘

Test result: OK. Total tests: 3; passed: 3; failed: 0
  • Test Name: Fully qualified name of the test function
  • Time: Execution time in seconds
  • Gas Used: Gas units consumed by the test

CSV Output

For programmatic analysis or importing into spreadsheets, use the csv option:

sui move test -s csv

This produces comma-separated output:

test_name,time_ns,gas_used
book::my_module::test_simple_operation,3381750,1
book::my_module::test_complex_operation,8454125,59
book::my_module::test_with_objects,3905625,25

The time is in nanoseconds, which allows for more precise measurements when comparing similar operations.

Gas Limits

Use the -i or --gas-limit flag to set a maximum gas budget for tests. Tests exceeding this limit will timeout:

sui move test -i 50

Output when a test exceeds the gas limit:

[ PASS    ] book::my_module::test_simple_operation
[ TIMEOUT ] book::my_module::test_complex_operation
[ PASS ] book::my_module::test_with_objects

Test failures:

Failures in book::my_module:

┌── test_complex_operation ──────
│ Test timed out
└──────────────────

This is useful for:

  • Identifying expensive operations: Find tests that consume unexpected amounts of gas
  • Enforcing gas budgets: Ensure critical paths stay within acceptable limits
  • Testing gas exhaustion: Verify your code handles out-of-gas scenarios correctly (see Expected Failures)

Comparing Implementations

Use statistics to compare gas consumption between different implementations:

module book::comparison;

use std::unit_test::assert_eq;

public fun sum_loop(n: u64): u64 {
let mut sum = 0;
n.do!(|i| sum = sum + i);
sum
}

public fun sum_formula(n: u64): u64 {
n * (n - 1) / 2
}

#[test]
fun test_sum_loop() {
let result = sum_loop(100);
assert_eq!(result, 4950);
}

#[test]
fun test_sum_formula() {
let result = sum_formula(100);
assert_eq!(result, 4950);
}

Running with statistics reveals the difference:

sui move test -s comparison
┌────────────────────────────────────┬────────────┬───────────────────────────┐
│ Test Name │ Time │ Gas Used │
├────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::comparison::test_sum_loop │ 0.005 │ 201 │
├────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::comparison::test_sum_formula │ 0.002 │ 3 │
└────────────────────────────────────┴────────────┴───────────────────────────┘

Trace Analysis

For deeper profiling, you can generate execution traces from tests and visualize them with speedscope. This shows a flamegraph of gas consumption broken down by function calls, making it easy to spot exactly where gas is being spent.

Step 1: Generate Traces

Run tests with the --trace flag to produce trace files:

sui move test --trace

Trace files are written to the traces/ directory inside the package build folder.

Step 2: Generate a Gas Profile

Use sui analyze-trace with the gas-profile subcommand to convert a trace into a profile:

sui analyze-trace -p traces/<TRACE_FILE> gas-profile

This outputs a gas_profile_<TRACE_FILE>.json file in the current directory. You can specify a different output directory with the -o flag:

sui analyze-trace -p traces/<TRACE_FILE> gas-profile -o ./profiles

Step 3: Visualize with Speedscope

Install speedscope and open the profile:

npm install -g speedscope
speedscope gas_profile_<TRACE_FILE>.json

Speedscope provides three views:

  • Time Order: Shows the call stack from left to right in invocation order. Bar width corresponds to gas consumption.
  • Left Heavy: Groups repeated calls together, ordered by total gas consumption - useful for finding the most expensive code paths.
  • Sandwich: Lists gas consumption per function with Total (including called functions) and Self (function only) columns.

Further Reading