> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ton.org/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.ton.org/feedback

```json
{
  "path": "/contract-dev/blueprint/benchmarks",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Benchmarking performance

export const FenceTable = ({children}) => {
  return <pre style={{
    'font-family': 'monospace',
    'white-space': 'pre',
    'overflow-x': 'auto',
    'font-size': '14px'
  }}>
    {children}
  </pre>;
};

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

In TON, a contract's performance is defined by its gas consumption, so it's important to design your logic efficiently.
Unlike many other blockchains, TON also requires you to pay for storing contract data and forwarding messages between contracts.

## Gas consumption

As you develop and iterate on a contract, even small changes to its logic can affect both gas usage and data size. Monitoring these changes helps ensure that your contract remains efficient and cost-effective.

<Aside type="tip">
  For a deeper breakdown of how fees work in TON, refer to [Transaction fees](/foundations/fees)
</Aside>

## Gas metrics reporting

To simplify tracking changes in gas usage and data size,
we’ve introduced a reporting system that lets you collect and compare metrics across different versions of a contract.

To enable this, write test scenarios that cover the contract’s primary usage patterns and verify expected behavior. This approach is sufficient to gather relevant metrics, which you can later use to compare performance changes after updating the implementation.

Before running the tests, a store is created to collect metrics from all transactions generated during the tests. After test execution, the collected metrics are supplemented with [ABI information from the snapshot](https://github.com/ton-org/sandbox/blob/main/docs/collect-metric-api.md#abi-auto-mapping), and a report is generated based on this data.

While more [metrics are collected](https://github.com/ton-org/sandbox/blob/main/docs/collect-metric-api.md#snapshot-structure), the current report format includes `gasUsed`, `cells`, and `bits`, which correspond to the internal metrics `compute.phase`, `state.code`, and `state.data`.

## Metrics comparison example

To see how gas metrics can be collected and compared in practice, let’s walk through a complete example.

Start by creating a new project using `npm create ton@latest`:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npm create ton@latest -y -- sample --type func-counter --contractName Sample
cd sample
```

**Note:**

* The `-y` flag skips prompts and accepts defaults.
* `--type` specifies the template (e.g., `func-counter`).
* `--contractName` sets the contract name.

Alternatively, you can run:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npm create ton@latest sample
```

This command scaffolds a project with a basic counter contract at `contracts/sample.fc`.
It defines a simple stateful contract that stores an `id` and a `counter` and supports an `increase` operation.

```func title="sample.fc" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
#include "imports/stdlib.fc";

const op::increase = "op::increase"c;
global int ctx_id;
global int ctx_counter;

() load_data() impure {
    var ds = get_data().begin_parse();

    ctx_id = ds~load_uint(32);
    ctx_counter = ds~load_uint(32);

    ds.end_parse();
}

() save_data() impure {
    set_data(
        begin_cell()
            .store_uint(ctx_id, 32)
            .store_uint(ctx_counter, 32)
            .end_cell()
    );
}

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_empty?()) { ;; ignore all empty messages
        return ();
    }

    slice cs = in_msg_full.begin_parse();
    int flags = cs~load_uint(4);
    if (flags & 1) { ;; ignore all bounced messages
        return ();
    }

    load_data();

    int op = in_msg_body~load_uint(32);
    int query_id = in_msg_body~load_uint(64);

    if (op == op::increase) {
        int increase_by = in_msg_body~load_uint(32);
        ctx_counter += increase_by;
        save_data();
        return ();
    }

    throw(0xffff);
}

int get_counter() method_id {
    load_data();
    return ctx_counter;
}

int get_id() method_id {
    load_data();
    return ctx_id;
}
```

### Generate a gas report

Let’s now generate a gas usage report for the contract.

Run the following command:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npx blueprint test --gas-report
```

This runs your tests with gas tracking enabled and outputs a `gas-report.json` with transaction metrics.

<FenceTable>
  ...
  PASS  Comparison metric mode: gas depth: 1
  Gas report write in 'gas-report.json'
  ┌───────────┬──────────────┬───────────────────────────┐
  │           │              │          current          │
  │ Contract  │    Method    ├──────────┬────────┬───────┤
  │           │              │ gasUsed  │ cells  │ bits  │
  ├───────────┼──────────────┼──────────┼────────┼───────┤
  │           │  sendDeploy  │   1937   │   11   │  900  │
  │           ├──────────────┼──────────┼────────┼───────┤
  │           │     send     │   515    │   11   │  900  │
  │  Sample   ├──────────────┼──────────┼────────┼───────┤
  │           │ sendIncrease │   1937   │   11   │  900  │
  │           ├──────────────┼──────────┼────────┼───────┤
  │           │  0x7e8764ef  │   2681   │   11   │  900  │
  └───────────┴──────────────┴──────────┴────────┴───────┘
</FenceTable>

### Storage fee calculation

You can use the `cells` and `bits` values from the report to estimate the **storage fee** for your contract.
Here’s the formula:

```text theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
storage_fee = ceil(
                  (account.bits * bit_price
                  + account.cells * cell_price)
               * time_delta / 2 ** 16)
```

To try this in practice, use the [calculator example](/foundations/fees).

### Regenerate the gas report

Note that the `op::increase` method appears in the report as the raw opcode `0x7e8764ef`.
To display a human-readable name in the report, update the generated `contract.abi.json` by replacing the raw opcode with the name **increase** in both the `messages` and `types` sections:

```diff theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
--- a/contract.abi.json
+++ b/contract.abi.json
@@ -6,13 +6,13 @@
         "receiver": "internal",
         "message": {
           "kind": "typed",
-          "type": "0x7e8764ef"
+          "type": "increase"
         }
       }
     ],
     "types": [
       {
-        "name": "0x7e8764ef",
+        "name": "increase",
         "header": 2122802415
       }
     ],
```

Once you've updated the `contract.abi.json` file, rerun the command to regenerate the gas report:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npx blueprint test --gas-report
```

Now the method name appears in the report as `increase`, making it easier to read:

<FenceTable>
  ...
  │           ├──────────────┼──────────┼────────┼───────┤
  │           │   increase   │   2681   │   11   │  900  │
  └───────────┴──────────────┴──────────┴────────┴───────┘
</FenceTable>

### Save a snapshot for future comparison

To track how gas usage evolves, you can create a named snapshot of the current metrics. This allows you to compare future versions of the contract against this baseline:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npx blueprint snapshot --label "v1"
```

This creates a snapshot file in `.snapshot/`:

```text theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
...
PASS  Collect metric mode: "gas"
Report write in '.snapshot/1749821319408.json'
```

### Optimize the contract and compare the metrics

Let’s try a simple optimization — adding the `inline` specifier to some functions.

<Aside type="note">
  An [inline specifier](/languages/func/functions#inline-specifier) is directly substituted into the code wherever it’s called, which can help reduce gas usage by eliminating the overhead of a function call.
</Aside>

Update your contract like this:

```diff theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
--- a/contracts/sample.fc
+++ b/contracts/sample.fc

-() load_data() impure {
+() load_data() impure inline {

-() save_data() impure {
+() save_data() impure inline {

-() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
+() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure inline {
```

Now regenerate the gas report. Since we already created a snapshot labeled `v1`, this report will include a comparison with the previous version:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npx blueprint test --gas-report
```

You see a side-by-side comparison of gas usage before and after the change:

<FenceTable>
  PASS  Comparison metric mode: gas depth: 2
  Gas report write in 'gas-report.json'
  ┌───────────┬──────────────┬─────────────────────────────────────────┬───────────────────────────┐
  │           │              │                 current                 │            v1             │
  │ Contract  │    Method    ├──────────────┬───────────┬──────────────┼──────────┬────────┬───────┤
  │           │              │   gasUsed    │   cells   │     bits     │ gasUsed  │ cells  │ bits  │
  ├───────────┼──────────────┼──────────────┼───────────┼──────────────┼──────────┼────────┼───────┤
  │           │  sendDeploy  │  1937 same   │ 7 -36.36% │ 1066 +18.44% │   1937   │   11   │  900  │
  │           ├──────────────┼──────────────┼───────────┼──────────────┼──────────┼────────┼───────┤
  │           │     send     │ 446 -13.40%  │ 7 -36.36% │ 1066 +18.44% │   515    │   11   │  900  │
  │  Sample   ├──────────────┼──────────────┼───────────┼──────────────┼──────────┼────────┼───────┤
  │           │ sendIncrease │  1937 same   │ 7 -36.36% │ 1066 +18.44% │   1937   │   11   │  900  │
  │           ├──────────────┼──────────────┼───────────┼──────────────┼──────────┼────────┼───────┤
  │           │   increase   │ 1961 -26.86% │ 7 -36.36% │ 1066 +18.44% │   2681   │   11   │  900  │
  └───────────┴──────────────┴──────────────┴───────────┴──────────────┴──────────┴────────┴───────┘
</FenceTable>

## Project setup instructions

If your project already exists, you need to configure **jest** to collect gas metrics.
You can do this in one of two ways:

#### Option 1: update the existing `jest.config.ts`

Add the necessary environment and reporter settings:

```diff title="jest.config.ts" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import type { Config } from 'jest';

const config: Config = {
    preset: 'ts-jest',
+    testEnvironment: '@ton/sandbox/jest-environment',
    testPathIgnorePatterns: ['/node_modules/', '/dist/'],
+    reporters: [
+        'default',
+        ['@ton/sandbox/jest-reporter', {}],
+    ]
};

export default config;
```

<Aside type="tip">
  See the full list of options in the [Sandbox jest config docs](https://github.com/ton-org/sandbox/blob/main/README.md#setup-in-jestconfigts).
</Aside>

#### Option 2: create a separate config `gas-report.config.ts`

If you prefer not to modify your main `jest.config.ts`, you can create a dedicated config file:

```ts title="gas-report.config.ts" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import config from './jest.config';

// use filter tests if needed, see https://jestjs.io/docs/cli#--testnamepatternregex
// config.testNamePattern = '^Foo should increase counter$'
config.testEnvironment = '@ton/sandbox/jest-environment'
config.reporters = [
    ['@ton/sandbox/jest-reporter', {
    }],
]
export default config;
```

When using this separate config, pass it using the `--config` option:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
npx blueprint test --gas-report -- --config gas-report.config.ts
npx blueprint snapshot --label "v2" -- --config gas-report.config.ts
```

## Collect metrics manually

You can collect metrics manually using the low-level API from `@ton/sandbox`.

```typescript title="collect-metrics.ts" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import {
    Blockchain,
    createMetricStore,
    makeSnapshotMetric,
    resetMetricStore
} from '@ton/sandbox';

const store = createMetricStore();

async function someDo() {
    const blockchain = await Blockchain.create();
    const [alice, bob] = await blockchain.createWallets(2);
    await alice.send({ to: bob.address, value: 1 });
}

async function main() {
    resetMetricStore();
    await someDo();
    const metric = makeSnapshotMetric(store);
    console.log(metric);
}

main().catch((error) => {
    console.log(error.message);
});
```

For more details, see the [Collect Metric API documentation](https://github.com/ton-org/sandbox/blob/main/docs/collect-metric-api.md#example).
