Don’t under-provision your Lambda functions! — or: how I learnt to stop worrying about Lambda memory costs
AWS Lambda is a fantastic service that we utilize for almost all aspects of the platform powering SquaredUp. It’s fair to say it’s the largest bit of infrastructure we provision, and by some estimates, at any time we have well over 1000 Lambda functions deployed, being used in various forms (like developer branch testing). With Lambda comprising upwards of 90% of our core application code, ensuring our Lambda functions are running as lean and efficiently as possible is one of the key things we look out for.
AWS gives you a small amount of control over the configuration of your Lambda functions and there are a number of things one can tweak which can have an impact of the speed (and cost!) of your function invocations:
- Language/Runtime¹
- Provisioned concurrency
- Memory allocation
You’ll notice there is nothing here around CPU. “Can’t I allocate more CPU cores?” No. Well…yes. It’s complicated.
According to AWS,
“…Lambda allocates CPU power proportional to the amount of memory provisioned”² — making memory configuration the “principal lever available to Lambda developers for controlling the performance of a function”
(AWS’ words, not mine!). Adding more memory proportionally increases the amount of CPU, increasing the overall computational power available to the Lambda function.
By default, Lambda functions created via the AWS portal are configured with the lowest possible memory allocation — 128MB. And since AWS Lambda billing is based on what they term “GB-Seconds/month”, it’s immediately obvious to anyone entering the Lambda ecosystem that if they touch that memory setting, and increase it, they are going to be charged more money. And no one wants that, right?
Hold on.
The second word in that scary sounding ‘GB-Seconds/Month’ is a unit of time. Meaning if we can get our Lambda functions to run faster, we’ll actually be saving money. And as we all know, when we throw more memory and CPU at a computing problem, it usually runs faster! And evidence backs this up — take for example 1000 invocations of a function that computes prime numbers. Such a function may have the following average durations at different memory levels:
| Memory | Duration | Cost |
|---------|----------|-----------|
| 128 MB | 11.722 s | $0.024628 |
| 512 MB | 6.678 s | $0.028035 |
| 1024 MB | 3.194 s | $0.026830 |
| 1536 MB | 1.465 s | $0.024638 |
But how much memory do you need to allow your lambda functions to run faster? Is there a point of diminishing returns? What is the break-even point? As we increase memory, the cost of those ‘GB-seconds’ increases too!
Enter the AWS Lambda Power Tuning project.
Choosing the memory allocated to Lambda functions is an optimization process that balances speed (duration) and cost. While you can manually run tests on functions by selecting different memory allocations and measuring the time taken to complete, the Lambda Power Tuning project can automate this process, running your actual Lambda functions at varying memory footprints, measuring and reporting both the performance and cost. It uses AWS Step Functions under the hood and, once run, even gives you some lovely graphs which make it easy to identify the ideal memory foot print for a given Lambda function (and remember — each Lambda function will produce different results, so this really will tailor that memory configuration for each individual Lambda!)
So remember — don’t be scared to increase that memory allocation, but before dialing it all the way to 11, utilize the Power Tuning tool to find the sweet spot that works for your exact Lambda usage.