Reading30 min read·Module 3High exam weight

AWS Lambda Performance Optimization

Key concepts

  • Memory and timeout settings

  • Cold starts and warm starts

  • Provisioned concurrency

  • Lambda layers

  • VPC configuration impact

Overview

AWS Lambda is a serverless compute service that automatically scales and manages infrastructure. However, performance optimization requires understanding memory allocation, cold start mitigation, and architectural choices. Optimizing Lambda performance is critical for latency-sensitive applications and appears frequently on the SAA-C03 exam.

Core Concept

Lambda performance optimization has three key levers: Memory allocation (also controls CPU, more memory = faster execution but higher cost), Cold start mitigation (Provisioned Concurrency, SnapStart, or keeping functions warm), and Architecture choice (ARM Graviton2 offers 19% better performance at 20% lower cost). Key rule: Memory scales linearly with cost, but execution time may decrease non-linearly.

Exam Tip

Exam signals: 'sub-100ms latency required' → Provisioned Concurrency, 'Java cold starts' → SnapStart, 'cost optimization + performance' → ARM Graviton2, 'CPU-intensive workload' → increase memory for more CPU. Remember: more memory = more CPU but higher $/GB-sec cost.

Key Concepts

Memory and CPU Allocation

Lambda Memory vs CPU Performance
Figure 1: Memory Configuration Impact on Performance

Memory-CPU-Performance Relationship

Memory allocation directly controls CPU power

CPU Scaling:

  • 128 MB to 1,769 MB: CPU scales linearly with memory
  • 1,769 MB = 1 full vCPU
  • 1,769 MB to 10,240 MB (max): Up to 6 vCPUs
  • At 1,769 MB, you get 1 full vCPU for entire duration
  • Beyond 1,769 MB, you get multiple vCPUs (multi-threading benefits)

Performance Impact:

Example: CPU-bound image processing task
- 128 MB (0.08 vCPU): 10,000ms execution
- 512 MB (0.3 vCPU): 3,000ms execution
- 1,024 MB (0.6 vCPU): 1,700ms execution
- 1,769 MB (1.0 vCPU): 1,000ms execution
- 3,008 MB (1.7 vCPU): 650ms execution

Cost Formula:

Cost = (Memory in GB) × (Duration in seconds) × Price per GB-second
Price = $0.0000166667 per GB-second

Example comparison:
- 128 MB, 10s: 0.125 × 10 × $0.0000166667 = $0.000021
- 1,024 MB, 1.7s: 1 × 1.7 × $0.0000166667 = $0.000028
- 3,008 MB, 0.65s: 2.94 × 0.65 × $0.0000166667 = $0.000032

Key Insight: Higher memory can REDUCE total cost if execution time decreases proportionally more than memory increases.

Finding the Optimal Memory Configuration

Use AWS Lambda Power Tuning

How It Works:

  1. Runs function with multiple memory configurations (128MB to 10GB)
  2. Measures execution time and cost for each
  3. Generates visualization showing optimal point
  4. Supports different optimization strategies (cost, speed, balanced)

Typical Optimization Patterns:

CPU-Bound Workloads:

  • Image/video processing
  • Data transformation
  • Cryptographic operations
  • Compression/decompression
  • Strategy: Increase memory significantly (1,769MB+) for more CPU

I/O-Bound Workloads:

  • Database queries
  • API calls to external services
  • S3 operations
  • Strategy: Moderate memory (512-1,024MB), focus on async operations

Memory-Intensive Workloads:

  • Large data processing
  • ML inference with large models
  • In-memory caching
  • Strategy: Allocate based on actual memory needs, not CPU needs
SHLambda Power Tuning Deployment
# Deploy Lambda Power Tuning via SAM
git clone https://github.com/alexcasalboni/aws-lambda-power-tuning.git
cd aws-lambda-power-tuning
sam deploy --guided

# Run power tuning for your function
aws stepfunctions start-execution \
  --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:powerTuningStateMachine \
  --input '{
    "lambdaARN": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
    "powerValues": [128, 256, 512, 1024, 1536, 2048, 3008],
    "num": 50,
    "payload": {},
    "parallelInvocation": true,
    "strategy": "balanced"
  }'

# Analyze results from Step Functions execution output
# Results include visualization URL and optimal memory recommendation

# Apply optimal memory configuration
aws lambda update-function-configuration \
  --function-name my-function \
  --memory-size 1536

Cold Start Optimization

Lambda Cold Start Mitigation Strategies
Figure 2: Cold Start Mitigation Techniques

Understanding Cold Starts

Cold Start = Time to initialize new execution environment

Cold Start Phases:

  1. Download Code: Lambda retrieves deployment package (~100ms for <50MB)
  2. Start Runtime: Initialize runtime environment (varies by runtime)
  3. Initialize Code: Run initialization code outside handler (custom)
  4. Invoke Handler: First invocation

Cold Start Duration by Runtime (typical): | Runtime | Cold Start Duration | |---------|-------------------| | Python 3.12 | 100-200ms | | Node.js 20 | 150-250ms | | Go | 100-150ms | | Rust | 100-150ms | | Java 21 (no SnapStart) | 2,000-10,000ms | | Java 21 (with SnapStart) | 200-600ms | | .NET 8 | 500-2,000ms |

Factors Affecting Cold Start:

  • Runtime choice (compiled vs interpreted)
  • Deployment package size
  • VPC configuration (adds 1-10 seconds)
  • Memory allocation (more memory = faster init)
  • Number of dependencies/libraries

Cold Start Mitigation Strategies

1. Provisioned Concurrency

  • Pre-initialized execution environments
  • Always warm, no cold starts
  • Responds in double-digit milliseconds
  • Cost: ~$0.0000041667 per GB-second (provisioned)
  • Use when: Consistent traffic, latency <100ms required

2. Lambda SnapStart (Java/Python/.NET)

  • Creates snapshot after initialization phase
  • Restores from snapshot instead of cold start
  • Reduces Java cold starts from 6s to <1s (10x faster)
  • Available for Java 11/17/21, Python 3.12+, .NET 8
  • Cost: No additional cost except snapshot storage ($0.00000309 per GB-month)
  • Use when: Java workloads with frequent cold starts

3. Keep Functions Warm

  • Scheduled EventBridge rule invokes function every 5 minutes
  • Maintains warm execution environment
  • Cost: Minimal (just invocation + short duration costs)
  • Use when: Budget-constrained, inconsistent traffic

4. Optimize Deployment Package

  • Minimize package size (<50MB ideal, <250MB max)
  • Remove unnecessary dependencies
  • Use Lambda Layers for shared libraries
  • Impact: Faster download phase (100ms → 50ms)

5. Optimize Initialization Code

  • Move SDK initialization outside handler
  • Lazy-load heavy dependencies
  • Use connection pooling
  • Cache computed values

6. Avoid VPC Unless Required

  • VPC adds 1-10 seconds to cold start
  • Use VPC only when accessing VPC resources
  • Consider NAT Gateway for internet access from Lambda
SHProvisioned Concurrency Configuration
# Enable Provisioned Concurrency for a function version
aws lambda put-provisioned-concurrency-config \
  --function-name my-function \
  --qualifier v1 \
  --provisioned-concurrent-executions 100

# Configure Auto Scaling for Provisioned Concurrency
aws application-autoscaling register-scalable-target \
  --service-namespace lambda \
  --resource-id function:my-function:v1 \
  --scalable-dimension lambda:function:ProvisionedConcurrency \
  --min-capacity 50 \
  --max-capacity 200

# Create scaling policy (target tracking)
aws application-autoscaling put-scaling-policy \
  --service-namespace lambda \
  --resource-id function:my-function:v1 \
  --scalable-dimension lambda:function:ProvisionedConcurrency \
  --policy-name my-scaling-policy \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 0.70,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "LambdaProvisionedConcurrencyUtilization"
    }
  }'

# Schedule Provisioned Concurrency (business hours only)
# Scale up at 8 AM
aws application-autoscaling put-scheduled-action \
  --service-namespace lambda \
  --resource-id function:my-function:v1 \
  --scalable-dimension lambda:function:ProvisionedConcurrency \
  --scheduled-action-name scale-up-morning \
  --schedule "cron(0 8 ? * MON-FRI *)" \
  --scalable-target-action MinCapacity=100,MaxCapacity=200

# Scale down at 6 PM
aws application-autoscaling put-scheduled-action \
  --service-namespace lambda \
  --resource-id function:my-function:v1 \
  --scalable-dimension lambda:function:ProvisionedConcurrency \
  --scheduled-action-name scale-down-evening \
  --schedule "cron(0 18 ? * MON-FRI *)" \
  --scalable-target-action MinCapacity=0,MaxCapacity=0
SHSnapStart Configuration
# Enable SnapStart for Java function
aws lambda update-function-configuration \
  --function-name my-java-function \
  --snap-start ApplyOn=PublishedVersions

# Publish new version with SnapStart
aws lambda publish-version \
  --function-name my-java-function \
  --description "Version with SnapStart enabled"

# Priming hook in Java code (CRaC)
import org.crac.Context;
import org.crac.Core;
import org.crac.Resource;

public class MyHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent>, Resource {

    public MyHandler() {
        Core.getGlobalContext().register(this);
    }

    @Override
    public void beforeCheckpoint(Context<? extends Resource> context) {
        // Priming logic: preload classes, warm up connections
        System.out.println("Priming before snapshot");
        // Initialize expensive resources here
        warmUpConnections();
        preloadClasses();
    }

    @Override
    public void afterRestore(Context<? extends Resource> context) {
        System.out.println("Restored from snapshot");
        // Re-establish connections if needed
    }

    // Handler implementation...
}

ARM Graviton2 Architecture

Lambda ARM Graviton2 vs x86 Performance
Figure 3: ARM Graviton2 Performance and Cost Benefits

AWS Graviton2 for Lambda

ARM-based processors for Lambda functions

Performance Benefits:

  • Up to 19% better performance for compute-intensive workloads
  • Up to 34% better price-performance overall
  • Better for multi-threaded workloads
  • Better for I/O-intensive operations

Cost Benefits:

  • 20% lower duration charges vs x86
  • Same request charges ($0.20 per 1M requests)
  • Same free tier (1M requests + 400K GB-seconds)
  • Duration: $0.0000133334 per GB-second (vs $0.0000166667 for x86)

Cost Comparison Example:

Workload: 10M requests/month, 512MB, 200ms avg duration
GB-seconds: 10M × 0.5GB × 0.2s = 1,000,000 GB-seconds

x86 Cost:
- Requests: 10M × $0.0000002 = $2.00
- Duration: 1M × $0.0000166667 = $16.67
- Total: $18.67/month

ARM (Graviton2) Cost:
- Requests: 10M × $0.0000002 = $2.00
- Duration: 1M × $0.0000133334 = $13.33
- Total: $15.33/month
- Savings: $3.34/month (18% reduction)

When to Use ARM:

  • ✅ CPU-intensive workloads (encoding, compression, encryption)
  • ✅ Multi-threaded applications
  • ✅ I/O-heavy operations
  • ✅ Cost optimization priority
  • ⚠️ Requires ARM-compatible dependencies
  • ❌ x86-specific native libraries

Migrating to ARM Graviton2

Migration Approaches by Language:

Interpreted Languages (Easy):

  • Python: Change architecture setting, no code changes
  • Node.js: Change architecture setting, rebuild native modules
  • Ruby: Change architecture setting, test dependencies

Compiled Languages (Requires Rebuild):

  • Go: Compile with GOOS=linux GOARCH=arm64
  • Rust: Cross-compile for aarch64-unknown-linux-gnu
  • Java: Recompile for ARM64 (if native libraries used)

Container Images:

  • Rebuild Docker images for linux/arm64 platform
  • Use multi-architecture images for flexibility
  • Test thoroughly before production

Dependencies to Check:

  • Native extensions (Python: numpy, pandas, Pillow)
  • Database drivers (psycopg2, mysql-connector)
  • C/C++ libraries
  • OS-specific binaries

Testing Strategy:

  1. Create ARM version of function
  2. Run integration tests
  3. Performance benchmark (compare with x86)
  4. Gradually shift traffic (weighted aliases)
  5. Monitor for errors/performance issues
SHMigrating to ARM Graviton2
# Check current architecture
aws lambda get-function-configuration \
  --function-name my-function \
  --query 'Architectures'

# Update function to ARM architecture
aws lambda update-function-configuration \
  --function-name my-function \
  --architectures arm64

# For container images - build multi-arch
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t my-lambda:latest \
  --push .

# Create ARM-specific layer
# Example: Python dependencies for ARM
docker run --rm \
  --platform linux/arm64 \
  -v "$PWD":/var/task \
  public.ecr.aws/lambda/python:3.12 \
  pip install -r requirements.txt -t python/

zip -r layer.zip python/

aws lambda publish-layer-version \
  --layer-name my-dependencies-arm \
  --compatible-runtimes python3.12 \
  --compatible-architectures arm64 \
  --zip-file fileb://layer.zip

# A/B testing with weighted aliases
# Create alias with 90% x86, 10% ARM
aws lambda create-alias \
  --function-name my-function \
  --name prod \
  --function-version $X86_VERSION \
  --routing-config AdditionalVersionWeights={$ARM_VERSION=0.1}

# Monitor performance differences
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda \
  --metric-name Duration \
  --dimensions Name=FunctionName,Value=my-function Name=Resource,Value=my-function:$ARM_VERSION \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-31T23:59:59Z \
  --period 3600 \
  --statistics Average,Maximum,Minimum

Best Practices

  1. Right-Size Memory: Use Lambda Power Tuning to find optimal memory configuration
  2. Choose Right Runtime: Go/Rust for lowest cold starts, Python/Node.js for balance
  3. Minimize Package Size: Keep deployment packages <50MB, use layers for dependencies
  4. Use SnapStart for Java: Reduces Java cold starts from 6s to <1s
  5. Schedule Provisioned Concurrency: Only provision during peak hours to save cost
  6. Migrate to ARM: 20% cost savings with minimal migration effort for most workloads
  7. Avoid VPC Unless Needed: VPC adds significant cold start latency
  8. Optimize Initialization: Move SDK clients and connections outside handler
  9. Use Connection Pooling: Reuse database connections across invocations
  10. Monitor Cold Start Rate: CloudWatch metric InitDuration tracks cold starts

Common Exam Scenarios

Exam Scenario Decision Guide

ScenarioRecommended SolutionKey Reasoning
API requiring <50ms response timeProvisioned ConcurrencyEliminates cold starts, sub-100ms response guaranteed
Java Spring Boot function with 6s cold startsLambda SnapStartReduces Java cold starts to <1s, no provisioning cost
CPU-intensive image processing, cost-sensitiveARM Graviton2 + increase memory20% lower cost, more CPU with higher memory
Node.js function, variable traffic, budget-constrainedStandard Lambda + keep-warm patternEventBridge rule keeps function warm at low cost
Python data processing taking 30s at 512MBIncrease to 2048MBMore CPU reduces duration, may lower total cost
Java function cold start 10s, consistent trafficSnapStart + Provisioned ConcurrencySnapStart reduces init time, Provisioned ensures always warm
1000 invocations/minute, each 200ms, optimize costARM Graviton2High volume benefits from 20% cost reduction
Lambda in VPC has 5s cold startsRemove VPC or use VPC with NAT GatewayVPC adds latency, use VPC endpoints or remove if not needed

Common Pitfalls

Over-Provisioning Memory

Allocating maximum memory (10GB) without testing wastes money. Lambda charges by GB-second, so 10GB costs 78x more than 128MB for same duration. Always benchmark with Lambda Power Tuning before choosing memory size.

24/7 Provisioned Concurrency

Running Provisioned Concurrency around the clock for functions that only need low latency during business hours wastes money. Use scheduled scaling to provision only during peak periods (e.g., 8 AM - 6 PM).

Ignoring ARM Compatibility

Migrating to ARM without checking dependencies can break functions. Native libraries (C/C++ extensions) may not have ARM builds. Always test in staging environment before production migration.

VPC for Everything

Adding Lambda to VPC by default adds 1-10 seconds to cold starts. Only use VPC when accessing VPC-only resources (RDS, ElastiCache). For AWS services (DynamoDB, S3), use VPC endpoints or public endpoints.

Quick Reference

Performance Optimization Checklist

OptimizationImpactEffortCost
Right-size memory20-50% faster + costLowVariable
ARM Graviton219% faster + 20% cheaperLow-Medium-20%
SnapStart (Java)10x faster cold startsLow~$0
Provisioned ConcurrencyEliminate cold startsLow$$$
Reduce package size50-100ms faster downloadMedium$0
Optimize init code100-500ms fasterMedium-High$0
Remove VPC1-10s faster cold startLow$0

Memory-CPU Mapping

Memory (MB)vCPU EquivalentUse Case
1280.08 vCPUMinimal I/O operations
5120.3 vCPULight processing
1,0240.6 vCPUModerate processing
1,7691.0 vCPUFull single-threaded CPU
3,0081.7 vCPUMulti-threaded workloads
10,2406.0 vCPUHeavy parallel processing

CLI Quick Reference

# Update memory configuration
aws lambda update-function-configuration \
  --function-name my-function \
  --memory-size 1536

# Enable ARM architecture
aws lambda update-function-configuration \
  --function-name my-function \
  --architectures arm64

# Enable SnapStart (Java)
aws lambda update-function-configuration \
  --function-name my-java-function \
  --snap-start ApplyOn=PublishedVersions

# Configure Provisioned Concurrency
aws lambda put-provisioned-concurrency-config \
  --function-name my-function \
  --qualifier v1 \
  --provisioned-concurrent-executions 50

Test Your Knowledge

Q

A Python Lambda function processes uploaded images (resize, compress). At 512MB, execution takes 3 seconds. The team wants to optimize cost. What should they do FIRST?

AIncrease memory to 3,008MB for more CPU power
BEnable Provisioned Concurrency to reduce cold starts
CRun Lambda Power Tuning to find optimal memory
DMigrate to ARM Graviton2 for 20% cost savings
Q

A Java Spring Boot Lambda function has 6-second cold starts, affecting user experience. The function receives steady traffic throughout business hours (9 AM - 5 PM). Which solution provides the BEST balance of performance and cost?

AEnable Lambda SnapStart only
BConfigure Provisioned Concurrency 24/7
CSnapStart + Scheduled Provisioned Concurrency (business hours)
DMigrate to Python runtime
Q

An e-commerce company runs a Lambda function behind API Gateway that requires <100ms response time. The function currently uses x86 architecture with 1024MB memory. How can they optimize for BOTH cost and performance?

ADecrease memory to 512MB to reduce cost
BEnable Provisioned Concurrency only
CMigrate to ARM Graviton2 + Enable Provisioned Concurrency
DUse Lambda SnapStart
Q

A Lambda function in a VPC takes 8 seconds for cold starts (5 seconds for VPC ENI setup). The function queries an RDS database. How can cold start time be reduced?

AEnable Lambda SnapStart
BIncrease memory to 10,240MB
CUse RDS Proxy instead of direct RDS connection
DRemove VPC and use public RDS endpoint

Further Reading


Sources:

Related services

Lambda