DEV Community

Cover image for SnapStart vs Native AOT: what actually happens to cold starts, warm latency, and your AWS bill
Oleksandr Shevchenko
Oleksandr Shevchenko

Posted on

SnapStart vs Native AOT: what actually happens to cold starts, warm latency, and your AWS bill

SnapStart vs Native AOT: what actually happens to cold starts, warm latency, and your AWS bill

If you run .NET on AWS Lambda, you've probably faced this fork in the road: enable SnapStart to shrink cold starts, or go all-in on Native AOT and skip the .NET runtime entirely. Both promise faster cold starts. Only one of them, in my tests, actually delivered.

I measured both configurations on the same 128 MB function shape, first with a couple of one-off samples, then with two proper batches (five cold requests and up to twelve warm requests per configuration), and finally ran the numbers through AWS's published pricing to see what it would cost at scale.

Spoiler: Native AOT won on cold latency, tied on warm latency, and won clearly on cost. Here's the data.

⚠️ Caveat up front: SnapStart isn't available on the Native AOT managed runtime, so this comparison necessarily ran AOT on Amazon Linux 2023 and SnapStart on the .NET 10 (C#/F#/PowerShell) managed runtime. That's a testing constraint, not a controlled experiment — the two configurations differ in more than just "SnapStart on or off." Treat this as directional evidence for this workload, not a universal verdict.

TL;DR

  • Cold requests (5 samples each): 471.72 ms mean for AOT vs 849.43 ms for SnapStart — AOT is 44.5% faster.
  • The gap is mostly first-execution, not snapshot restoration: 81.7% of the cold-latency difference comes from the first handler invocation, not the restore step.
  • Warm requests (8 AOT / 12 SnapStart): AOT has the lower median (35.68 vs 39.50 ms), SnapStart has the slightly lower mean (53.67 vs 57.32 ms) — no clear warm-latency winner in these small samples.
  • Cost: at 1M invocations/month and 1% cold starts, illustrative pricing puts AOT at $0.33/month vs SnapStart at $0.98/month per function — mostly because of SnapStart's caching and restoration fees, not compute.
  • Numbers below are small-sample measurements and a pricing model, not a guarantee for your workload. Verify before deciding.

1. Initial cold-start and single warm-request samples

Both configurations use 128 MB of allocated memory. Native AOT was tested on the Amazon Linux 2023 managed runtime; SnapStart was tested on the .NET 10 (C#/F#/PowerShell) managed runtime, since SnapStart is not available for the Native AOT runtime. This runtime difference is a testing constraint, not a controlled variable — the compared configurations differ in more than just SnapStart on/off.

Measurement SnapStart Native AOT
Cold initialization / restoration 346.09 ms 267.41 ms
First handler execution 496.66 ms 192.15 ms
Approximate cold-start total¹ 842.75 ms 459.56 ms
Cold billed duration 500 ms 460 ms
Warm handler execution 9.41 ms 36.82 ms
Warm billed duration 10 ms 37 ms
Maximum memory used 55 MB 50 MB

¹ Initialization/restoration plus execution; excludes API Gateway, client networking, and a separate authorizer.

Interpretation

  • Cold request: Native AOT was 383.19 ms faster. SnapStart took approximately 83% longer.
  • Warm request: SnapStart was 27.41 ms faster, approximately 74% shorter.
  • Most of the cold-request difference came from handler execution, not snapshot restoration:
    • Restoration versus initialization: 78.68 ms difference.
    • First handler execution: 304.51 ms difference.

That first-execution difference deserves investigation. Network reconnection, downstream service latency, and first-use initialization are possible explanations—not conclusions established by these logs.

Important limitation: these are one cold and one warm sample per configuration. They show what happened, but do not establish typical or p95 performance, or prove that SnapStart caused the warm improvement. Runtime/build details are based on the supplied deployment descriptions rather than independently established by these REPORT lines.

Source logs

summary="Raw CloudWatch REPORT lines (initial single-request samples)"

SnapStart:

REPORT RequestId: b23a929b-c7f9-404b-ad32-49210dad2fe0 Duration: 496.66 ms Billed Duration: 500 ms Memory Size: 128 MB Max Memory Used: 55 MB Restore Duration: 346.09 ms Billed Restore Duration: 3 ms
REPORT RequestId: fd64a0ca-b27b-4a58-bfab-06314199a0dc Duration: 9.41 ms Billed Duration: 10 ms Memory Size: 128 MB Max Memory Used: 55 MB
Enter fullscreen mode Exit fullscreen mode

Native AOT without SnapStart:

REPORT RequestId: e2055d28-ce2e-4c69-ba09-df58c8efec4b Duration: 192.15 ms Billed Duration: 460 ms Memory Size: 128 MB Max Memory Used: 50 MB Init Duration: 267.41 ms
REPORT RequestId: 0513e1ac-1715-4ea4-bd87-f3d885ad0b44 Duration: 36.82 ms Billed Duration: 37 ms Memory Size: 128 MB Max Memory Used: 50 MB
Enter fullscreen mode Exit fullscreen mode

Follow-up warm-request samples

The additional batch contains 8 Native AOT requests and 12 SnapStart requests. None of these REPORT lines includes Init Duration or Restore Duration, so they are consistent with warm invocations. REPORT lines alone do not rule out a suppressed initialization after a failure.

Metric SnapStart (12 requests) Native AOT (8 requests)
Median execution 39.50 ms 35.68 ms
Mean execution 53.67 ms 57.32 ms
Minimum execution 30.59 ms 23.71 ms
Maximum execution 181.04 ms 199.86 ms
Mean billed duration 54.25 ms 57.625 ms
Reported maximum memory used 55–58 MB 50–53 MB

Native AOT's median is 3.825 ms lower; SnapStart's median is approximately 10.7% higher. However, SnapStart's mean execution is 6.4% lower. Both datasets have slow observations that influence the means; all observations are retained. Max Memory Used is an execution-environment high-water mark, not an isolated measurement of memory consumed by each request.

These results do not establish a consistent warm-execution winner. They supersede the initial single-request warm comparison as the basis for the cost estimates below. Sample sizes are small and unequal, request equivalence has not been verified, and p95 estimates would be unstable.

Durations and billed durations from the follow-up batch, in supplied order (milliseconds):

summary="Raw warm-batch data (20 requests)"
AOT execution: 23.71, 199.86, 60.78, 38.82, 28.88, 44.28, 32.53, 29.66
AOT billed:    24, 200, 61, 39, 29, 45, 33, 30
Snap execution: 41.02, 33.65, 39.33, 181.04, 36.62, 40.43, 90.49, 39.67, 37.90, 41.12, 32.21, 30.59
Snap billed:    42, 34, 40, 182, 37, 41, 91, 40, 38, 42, 33, 31
Enter fullscreen mode Exit fullscreen mode

Follow-up cold-start samples (five per configuration)

Each SnapStart RESTORE_REPORT repeats the Restore Duration shown in its corresponding REPORT; it is counted once, not twice. Approximate Lambda-side cold latency is restoration or initialization plus handler execution, not billed duration.

Metric SnapStart Native AOT
Mean restoration / initialization 365.74 ms 296.80 ms
Mean first handler execution 483.69 ms 174.92 ms
Mean cold total 849.43 ms 471.72 ms
Median cold total 827.50 ms 440.35 ms
Cold total minimum–maximum 775.62–976.80 ms 432.07–605.38 ms
Mean billed duration 500.60 ms 472.40 ms
Reported maximum memory used 55 MB 50 MB

Native AOT's mean cold latency is 377.71 ms (44.5%) lower than SnapStart's. Equivalently, SnapStart's mean cold latency is approximately 80.1% higher than AOT's. Even the slowest AOT cold request in this batch is faster than the fastest SnapStart cold request. This is descriptive evidence from five samples each, not a population-level guarantee or a matched-pair experiment.

The mean gap has two components:

  • Restoration versus initialization: 68.94 ms.
  • First handler execution: 308.77 ms, accounting for approximately 81.7% of the total gap.

The first-handler penalty is consistent across this batch: SnapStart executions range from 460.01 to 500.75 ms, versus 149.83 to 212.37 ms for AOT. This supports investigating first-use work, network reconnection, and downstream call timing rather than attributing the whole difference to restoration. These logs do not identify the cause or prove the deployed build types.

Values in supplied order (milliseconds); totals combine fields within each request, not requests across configurations:

summary="Raw cold-start batch data (10 requests)"
Snap restore: 335.73, 333.19, 481.15, 363.02, 315.61
Snap execution: 491.77, 470.25, 495.65, 500.75, 460.01
Snap cold total: 827.50, 803.44, 976.80, 863.77, 775.62
Snap billed: 493, 509, 497, 502, 502
Snap billed restore: 1, 38, 1, 1, 41
AOT init: 277.92, 276.34, 282.24, 254.48, 393.01
AOT execution: 162.43, 164.18, 149.83, 185.78, 212.37
AOT cold total: 440.35, 440.52, 432.07, 440.26, 605.38
AOT billed: 441, 441, 433, 441, 606
Enter fullscreen mode Exit fullscreen mode

SnapStart's billed restore portion varies from 1 to 41 ms, while actual restoration takes 315.61 to 481.15 ms. A lower chargeable restore portion does not mean a faster response. Billed duration already includes that chargeable portion; do not add it again when calculating compute cost.

End-to-end Postman observations

The user reports that Native AOT responds faster in Postman for both cold and warm requests. No numerical Postman timings were supplied, so this is a qualitative observation, not a measured percentage improvement. Postman includes client/network overhead, API Gateway, authorization, and the backend handler; a single Lambda Duration does not cover that entire path.

2. Pros and cons for this application

Option Pros supported by these samples Cons / trade-offs
Native AOT Lower cold latency across both supplied batches; lower follow-up warm median; lower reported memory use; no SnapStart fees; user reports faster Postman responses Slightly higher follow-up warm mean and one 199.86 ms observation; AOT requires compatible libraries and trimming configuration
SnapStart Slightly lower follow-up warm mean and mean billed duration Higher follow-up warm median; slower cold samples and consistently longer first-handler execution in the new batch; snapshot caching and restoration fees; published-version management; still has cold restores

Neither provides guaranteed warm-like latency after idle periods.

3. Long-term pricing assumptions

The following estimates use rates published in the AWS Lambda pricing example, reviewed on 2026-09-15:

Charge Rate used
x86 execution $0.0000166667 / GB-second
Requests $0.20 / million
.NET SnapStart caching $0.0000015046 / GB-second
.NET SnapStart restoration $0.0001397998 / GB restored

These are illustrative public example rates, not a verified Ohio-region quote: the regional pricing lookup failed.

Assumptions:

  • One function, 128 MB = 0.125 GB.
  • One SnapStart version cached throughout a 30-day month.
  • Cold durations use the five-request follow-up batch's mean billed duration: SnapStart 500.60 ms, AOT 472.40 ms. Warm durations use the warm follow-up batch's mean billed duration: SnapStart 54.25 ms, AOT 57.625 ms. Initial single cold and warm samples remain historical context and are not pooled into these estimates.
  • These small samples are assumed representative only for illustration; they are not a reliable long-term traffic forecast.
  • Both alternatives have the same invocation count and cold-start percentage.
  • Each modeled cold SnapStart invocation corresponds to one snapshot restoration.
  • Excludes discounts, deployment/snapshot-initialization compute, API Gateway, logs, and downstream services.
  • Billing uses allocated memory, not maximum memory used.

Additional SnapStart fees at 128 MB

  • Caching: approximately $0.49/month per active version.
  • Restoration: approximately $0.0175 per 1,000 restores.
  • Seven functions with one cached version each: approximately $3.41/month, or $41.52 per 365-day year, for caching alone.

Old active versions can increase caching charges. Caching has a three-hour minimum per version.

The logged Billed Restore Duration: 3 ms contributes to ordinary duration billing; it does not replace the separate restoration fee.

Calculation model

Let N be monthly invocations and f the fraction experiencing a cold start. Durations below are billed seconds and costs are USD.

summary="Show the full cost formulas"
SnapStart compute = N × [f × 0.500600 + (1 − f) × 0.054250] × 0.125 × 0.0000166667
AOT compute       = N × [f × 0.472400 + (1 − f) × 0.057625] × 0.125 × 0.0000166667
Requests          = N × 0.20 / 1,000,000
SnapStart cache   = 0.125 × 2,592,000 × 0.0000015046
SnapStart restore = N × f × 0.125 × 0.0001397998

SnapStart total = SnapStart compute + Requests + SnapStart cache + SnapStart restore
AOT total       = AOT compute + Requests
Enter fullscreen mode Exit fullscreen mode

4. Estimated monthly cost

Includes execution, requests, and applicable SnapStart fees.

Invocations per function/month Cold-start percentage SnapStart Native AOT
100,000 0.1% $0.52 $0.032
100,000 1% $0.54 $0.033
1,000,000 0.1% $0.82 $0.32
1,000,000 1% $0.98 $0.33
1,000,000 10% $2.64 $0.41

At one million invocations/month and 1% cold starts, twelve such 30-day months cost approximately:

  • SnapStart: $11.81 per function
  • Native AOT: $3.94 per function

These are small absolute amounts at 128 MB, but multiply with function count, retained versions, and traffic. These per-function estimates should not be treated as per-API-request costs when each API request invokes both an authorizer and a handler.

Could SnapStart eventually be cheaper?

Potentially, if its slightly lower mean warm billed duration holds across many requests. Under this updated model:

  • With virtually no restores, roughly 69.3 million invocations/month are needed for warm-compute savings to offset caching.
  • Above approximately 0.040% cold invocations, restoration fees and cold-duration differences outweigh those warm savings even before adding caching.

These thresholds are highly sensitive to the measured durations and assumed pricing.

5. Project-level potential cost impact

This compares Native AOT without SnapStart against SnapStart enabled for seven Lambda functions, using the supplied logs as a duration proxy. It is an estimate of the Lambda cost difference, not the project's entire AWS bill.

Project assumptions

  • Seven functions, each allocated 128 MB.
  • One active cached version per function throughout a 30-day month: seven snapshots total.
  • 1% cold starts in both scenarios.
  • Mean billed durations from the supplied batches: 472.40 ms cold / 57.625 ms warm without SnapStart, versus 500.60 ms cold / 54.25 ms warm with SnapStart.
  • These durations are assumed to represent all seven functions; individual function timings have not been verified.
  • Uses the illustrative pricing rates in section 3, excluding discounts and deployment/snapshot-initialization compute.
  • Other infrastructure costs are excluded and assumed unchanged between scenarios.

Invocation counts below are totals across the project, not per function and not API request counts. An API request invoking both a JWT authorizer and a backend function produces two Lambda invocations. Scheduled/background invocations also count toward the total.

Monthly project Lambda costs

Total Lambda invocations/month Without SnapStart With SnapStart SnapStart extra/month
100,000 $0.03 $3.46 +$3.43
1,000,000 $0.33 $3.91 +$3.58
10,000,000 $3.29 $8.38 +$5.10

Totals and differences are rounded independently from unrounded calculations.

At one million total project invocations/month, twelve 30-day months cost approximately:

  • Without SnapStart: $3.94.
  • With SnapStart: $46.91.
  • Additional SnapStart cost: $42.97.

Cost breakdown at one million project invocations/month

Cost component Without SnapStart With SnapStart
Requests $0.200 $0.200
Execution $0.129 $0.122
Snapshot caching — seven versions $3.412
Snapshot restorations — 10,000 restores $0.175
Total $0.33 $3.91

The project's SnapStart caching component is 7 × $0.4874904 = $3.4124328/month. Execution, requests, and restoration charges use the project's total invocations only once; they are not multiplied by seven again. Retaining additional active versions increases caching charges.

Cost conclusion: at 1% cold starts, the modeled warm-execution saving is less than one cent per million project invocations and does not offset SnapStart caching and restoration fees. The supplied logs therefore favor disabling SnapStart on cost as well as observed cold latency, subject to these assumptions. This is not a recommendation to change deployments without reviewing individual functions and retained versions.

6. Recommendation

The current evidence favors Native AOT for cold latency across both supplied batches, warm median, and modeled cost. The five-request cold batch has mean totals of 471.72 ms for AOT versus 849.43 ms for SnapStart. The user also reports faster end-to-end Postman responses with Native AOT. SnapStart has a slightly lower follow-up warm mean, so these logs do not prove Native AOT is faster for every warm request.

Before deciding permanently, compare identical requests and dependencies across multiple cold starts and warm runs, recording median and p95 total API latency. Keep allocated memory, architecture, workload, and downstream configuration equivalent.

In particular, investigate why first-handler execution averages 483.69 ms with SnapStart versus 174.92 ms with AOT in the five-request cold batch. This accounts for approximately 81.7% of the mean cold-latency gap. Correlate client timings with authorizer and backend request IDs, and instrument downstream calls before selecting a code-level optimization.

Closing thoughts

For this workload, Native AOT beat SnapStart on cold latency, tied on warm latency, and came out cheaper once caching and restoration fees were modeled — without needing the extra moving parts of published versions and snapshot management. That said, the runtime mismatch noted above (Amazon Linux 2023 vs the .NET 10 managed runtime) means this isn't a clean, apples-to-apples benchmark, and five cold samples per configuration is a small dataset to build a permanent architectural decision on.

If you're weighing the same trade-off: measure your own workload, watch what happens to that first post-restore invocation, and run the numbers through your actual traffic and cold-start rate before committing.

Have you run a similar comparison, or seen different results with a heavier workload or larger memory allocation? I'd like to hear about it in the comments.

Top comments (0)