llms.txt
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 onchain 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.006 │ 998001 │
├────────────────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::my_module::test_complex_operation │ 0.007 │ 998068 │
├────────────────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::my_module::test_with_objects │ 0.006 │ 998001 │
└────────────────────────────────────────────────────────┴────────────┴───────────────────────────┘

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

Every test's total includes a large fixed base cost - even an empty test reports roughly 998000 gas units. When comparing tests, look at the difference between their totals rather than the absolute values.

CSV Output​

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

sui move test -s csv

This produces comma-separated output:

name,nanos,gas
book::my_module::test_simple_operation,5992125,998001
book::my_module::test_complex_operation,6870583,998068
book::my_module::test_with_objects,6022917,998001

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 1000

The limit is measured in internal execution gas units, which do not map one-to-one to the values in the Gas Used column - a trivial test that reports ~998000 gas passes comfortably with a limit of 1000.

Output when a test exceeds the gas limit:

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

Test failures:

Failures in book::my_module:

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

Test result: FAILED. Total tests: 3; passed: 2; failed: 1

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(1000);
assert_eq!(result, 499500);
}

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

Running with statistics reveals the difference:

sui move test comparison -s
┌────────────────────────────────────┬────────────┬───────────────────────────┐
│ Test Name │ Time │ Gas Used │
├────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::comparison::test_sum_loop │ 0.003 │ 998078 │
├────────────────────────────────────┼────────────┼───────────────────────────┤
│ book::comparison::test_sum_formula │ 0.001 │ 998001 │
└────────────────────────────────────┴────────────┴───────────────────────────┘

The loop costs 77 gas units on top of the base cost, while the formula adds nothing measurable.

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 in the package root (next to Move.toml).

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, which goes before the gas-profile subcommand:

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

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​

llms.txt