Home / AWS

AWSHow-toIntermediate

Optimization: Lambda performance and cost - memory power tuning, arm64, SnapStart and provisioned concurrency

Lambda cost is duration × memory, and CPU scales with memory, so the cheapest setting is often not the smallest. Measure with Power Tuning, move to arm64 where dependencies allow, reduce init time, use SnapStart or provisioned concurrency only where cold starts matter, and trim invocations you don't need.

Deepak Bhardwaj3 min read

Applies toAWS Lambda (Node.js, Python, Java, .NET and others), AWS Lambda Power Tuning, Lambda Insights, X-Ray

How Lambda is billed

cost ≈ invocations × request price + (duration in seconds × memory in GB) × GB-second price

CPU allocation rises with memory. A CPU-bound function at 256 MB may take 4× longer than at 1,024 MB. Same cost, slower. Or more memory can be both faster and cheaper.

1. Find the functions that matter

Cost Explorer grouped by resource (with resource-level data enabled), or a CloudWatch Metrics Insights query in the console:

SELECT SUM(Duration)
FROM SCHEMA("AWS/Lambda", FunctionName)
GROUP BY FunctionName
ORDER BY SUM() DESC
LIMIT 15

Top functions by total duration (and invocations) are where tuning pays.

2. Power Tuning

AWS Lambda Power Tuning (open-source Step Functions state machine) runs a function at several memory sizes and charts cost vs speed:

{
  "lambdaARN": "arn:aws:lambda:eu-west-1:123456789012:function:resize-image",
  "powerValues": [256, 512, 1024, 1536, 2048, 3008],
  "num": 50,
  "payload": { "bucket": "test-images", "key": "sample.jpg" },
  "parallelInvocation": true,
  "strategy": "balanced"
}

Use a realistic payload. For functions with side effects, point it at test resources.

3. arm64 (Graviton)

Switching the architecture to arm64 typically gives a lower GB-second price and often similar or better performance. Check native dependencies (compiled Python wheels, Node native modules, layers) have arm64 builds, then test:

aws lambda update-function-code --function-name resize-image --zip-file fileb://build-arm64.zip --architectures arm64

4. Reduce cold start and init time

Cause Fix
Large deployment package Remove unused dependencies; tree-shake (Node); slim layers
SDK clients created per invocation Create once outside the handler, reuse
Heavy framework start-up (Java/Spring, .NET) SnapStart (supported runtimes), lighter frameworks
VPC attachment Modern VPC networking removed most ENI cold-start delay; check other causes first
Loading config/secrets on every call Cache with TTL (Parameters and Secrets extension)

Look at Init Duration in REPORT log lines or Lambda Insights to measure.

5. SnapStart vs provisioned concurrency

SnapStart Provisioned concurrency
What Snapshot of initialised environment, restored on cold start Pre-initialised environments kept warm
Cost Supported runtimes; caching/restore charges may apply by runtime Pay for configured concurrency all the time it's on
Use when Init is slow and occasional cold starts are OK if fast Strict latency, predictable traffic (schedule with Application Auto Scaling)

Code that generates unique values or opens connections at init time needs care with SnapStart (runtime hooks).

6. Fewer, better invocations

  • Batch SQS/Kinesis records (BatchSize, MaximumBatchingWindowInSeconds) instead of one message per invocation.
  • Event filtering on event source mappings so irrelevant events never invoke the function.
  • Don't poll from Lambda on a schedule when an event exists.
  • Step Functions for waiting, not sleep inside Lambda.

7. Also check

  • Timeouts set close to real p99 duration, not 15 minutes "just in case".
  • Logs: JSON with WARN level in prod (logging is often a hidden cost).
  • Recursive loops: Lambda's recursion detection catches some, but design guards in.

Tip

Re-run Power Tuning after big code or dependency changes. The best memory setting moves when the workload changes from I/O-bound to CPU-bound.

Checklist

  • Top functions by duration and invocations identified
  • Power Tuning on the top ones with realistic payloads
  • arm64 where dependencies allow
  • Init time reduced; SnapStart or provisioned concurrency where justified
  • Batching and event filtering on stream/queue sources
  • Timeouts and log levels reviewed