Lambda
07 / 08

Performance, Concurrency & Cold Starts

Lambda: Performance, Concurrency & Cold Starts

Cold Start Anatomy

A cold start happens when Lambda initializes a new execution environment. The INIT phase: download code/container → start runtime → run module-level code. Can add 100ms–3s depending on runtime and code size.

Phase          | What happens                            | When
---------------|----------------------------------------|---------------------------
Download       | Fetch deployment package from S3/ECR   | Cold start only
Runtime init   | Start Node.js/Python/JVM process       | Cold start only
Handler init   | Run module-level code (your init code) | Cold start only
Invoke         | Run handler function                   | Every invocation

Typical cold start durations (simple function, no VPC):
  Node.js:  50-200ms
  Python:   100-300ms
  Java:     1-5s (JVM startup) → use SnapStart
  Go:       <50ms (compiled binary)
  Container: 1-10s (image pull)

Reducing Cold Starts

  • Provisioned Concurrency: keep N environments pre-initialized, no cold starts for those

  • SnapStart (Java only): snapshot JVM state after init, restore snapshot on cold start — 90% faster

  • Right-size memory: more memory = faster network/CPU, faster INIT phase

  • Minimize package size: smaller ZIP = faster download; use tree-shaking, remove unused deps

  • Avoid VPC unless needed: VPC adds ~100ms for ENI attachment (improved with Hyperplane ENI)

  • Move init code outside handler: DB connections, SDK clients, parsed config

# Provisioned Concurrency (warm environments always ready)
aws lambda put-provisioned-concurrency-config \
  --function-name my-api-handler \
  --qualifier v5 \
  --provisioned-concurrent-executions 10

# Scale Provisioned Concurrency with Application Auto Scaling
aws application-autoscaling register-scalable-target \
  --service-namespace lambda \
  --resource-id function:my-api-handler:v5 \
  --scalable-dimension lambda:function:ProvisionedConcurrency \
  --min-capacity 5 \
  --max-capacity 50

Concurrency Model

Concurrency = (invocations/second) × (avg duration in seconds)

Types:
  Unreserved concurrency: shared pool for all functions in the region (default 1000)
  Reserved concurrency:   cap a function to N — never exceeds this, guaranteed minimum
  Provisioned concurrency: pre-warmed environments (subset of reserved)

Account default: 1000 concurrent executions per region
Request increase: via Service Quotas console (up to tens of thousands)

Throttling (429):
  Sync invocations: error returned immediately to caller
  Async invocations: retried automatically 2 times with backoff, then DLQ/destination
# Set reserved concurrency (also limits function to 100 max)
aws lambda put-function-concurrency \
  --function-name my-api-handler \
  --reserved-concurrent-executions 100

# Set to 0 to completely throttle function (emergency disable)
aws lambda put-function-concurrency \
  --function-name my-api-handler \
  --reserved-concurrent-executions 0

ARM64 (Graviton2)

Lambda supports ARM64 architecture (AWS Graviton2). Up to 34% better price/performance for many workloads. Simple runtime change — no code changes for Node.js/Python.

aws lambda update-function-configuration \
  --function-name my-api-handler \
  --architectures arm64

Lambda Power Tuning

  • Open source tool (AWS Step Functions): runs your function at 10 different memory sizes, finds optimal cost/performance

  • Use it before choosing memory settings for production

  • Often: doubling memory doubles cost per second but halves duration → same or cheaper total cost

  • Run at: github.com/alexcasalboni/aws-lambda-power-tuning

Ephemeral Storage (/tmp)

import { writeFileSync, readFileSync, existsSync } from 'fs';

// /tmp is shared across warm invocations in the same environment
// Cache expensive downloads across invocations
const CACHE_PATH = '/tmp/model-cache.bin';

export const handler = async (event) => {
  if (!existsSync(CACHE_PATH)) {
    // Cold start: download and cache
    const modelData = await downloadModel();
    writeFileSync(CACHE_PATH, modelData);
  }
  const model = readFileSync(CACHE_PATH);
  return predict(model, event.input);
};

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free