Skip to content
Technology
Software Development
Cloud Services

Blog: Optimize Your AWS Lambda Functions for Cost and Performance

06/06/2023

In this blog post, we will dive deep into using AWS Lambda Power Tuning to optimize your Lambda functions for cost and performance. As serverless architectures continue to gain traction, the need to maximize the efficiency of Lambda functions is becoming more critical than ever. This tool helps developers find the optimal balance between performance and cost for their use case. By the end of this post, you will better understand how to use Lambda Power Tuning to find the best memory setting for your Lambda functions.

AWS Lambda is an event-driven, serverless computing platform provided by Amazon Web Services (AWS). It allows developers to run their code without managing servers, automatically scaling the compute capacity based on the workload. However, as applications grow, so do the costs and performance requirements. Finding the optimal balance between cost and performance can be challenging.

Disclaimer: Please note that following this tutorial may incur costs for AWS services, particularly AWS Lambda and AWS Step Functions. The charges will depend on the number of invocations, memory settings, execution time, and the duration of the step function executions. Review the AWS Lambda pricing and AWS Step Functions pricing before proceeding with the tutorial. Additionally, remember to delete or disable any resources you no longer need after completing the tutorial to avoid incurring ongoing costs.

The Problem: Cost and Performance Trade-offs

Lambda functions are billed based on the memory and compute time used during execution. Higher memory settings result in faster execution times but at a higher cost per invocation. Conversely, lower memory settings reduce costs but can lead to longer execution times. This trade-off creates a dilemma for developers: how to find the optimal balance between cost and performance?

AWS Lambda Power Tuning is an open-source tool that addresses this problem by systematically finding the best memory configuration for a given Lambda function. The tool allows developers to quickly and easily analyze various memory settings' cost and performance implications, helping them make informed decisions about their function's configuration.

Deploying the AWS Lambda Power Tuning Serverless Application

There are multiple ways to deploy AWS Lambda Power Tuning:

You can find all the tutorials for deploying the application here. The easiest way to start with Lambda Power Tuning is by using the Power Tuning Serverless Application.

 

To deploy the AWS Lambda Power Tuning Serverless Application:

  1. Visit the AWS Lambda Power Tuning Serverless Application page
  2. Click the "Deploy" button
  3. Log in to your AWS account if prompted
  4. Review and acknowledge the settings, then click "Deploy"

The CloudFormation template will create the necessary resources, including the step function.

 

The input parameters for the step function include:

  • lambdaARN: The Amazon Resource Name (ARN) of your Lambda function
  • powerValues: The memory settings you want to test
  • num: The number of invocations for each memory power value
  • payload: The input payload for the Lambda function
  • parallelInvocation: Whether to use parallel invocations (true is recommended)
  • strategy: The optimization strategy (cost, balanced, or speed)

Note: To minimize the cost, you can adjust the number of invocations (num) and the power values (powerValues) you use when testing your Lambda function with the Power Tuning tool.

Creating a CPU-Heavy Lambda Function: Fibonacci Example

In this section, we will create a simple CPU-heavy Lambda function using JavaScript as an example to demonstrate the power of AWS Lambda Power Tuning. We will use a naive Fibonacci algorithm, which is computationally expensive, to show how the Power Tuning tool can help optimize the cost and performance of such functions.

While there are more efficient algorithms and techniques to calculate Fibonacci numbers, such as memoization and matrix exponentiation, we will keep the example simple to focus on the AWS Lambda Power Tuning process. Using a naive approach, we can highlight the potential performance gains and cost savings the Power Tuning tool can provide. 

Here's the naive Fibonacci algorithm implemented as an AWS Lambda function in JavaScript:

const fibonacci = (number) => {
// Check if number is a positive integer
if (!Number.isInteger(number) || number < 0) {
// If not, throw an error with a helpful message
throw new Error('Invalid input. Please provide a positive integer.');
}

// Base case: return the number if it's 0 or 1
if (number <= 1) {
return number;
}

// Recursive case: return the sum of the previous two numbers in the sequence
return fibonacci(number - 1) + fibonacci(number - 2);
};

exports.handler = async (event) => {
try {
// Extract the value of 'number' from the event object
const { number } = event;

// Call the Fibonacci function to calculate the result
const fibonacciResult = fibonacci(number);

// Return the result as a JSON object with a 200 status code
return {
statusCode: 200,
body: JSON.stringify({ fibonacciResult }),
};
} catch (error) {
// If there was an error, return a 400 status code and the error message
return {
statusCode: 400,
body: error.message,
};
}
};

 

After creating this Lambda function, you can use its Amazon Resource Name (ARN) as input for the AWS Lambda Power Tuning tool.

arn_button

Configuring and Running the Step Function

Once the Power Tuning Serverless Application is deployed, you can configure and run the step function to test different memory settings for your Lambda function. Follow these steps:

    1. Navigate to the Step Functions console in the AWS Management Console.
    2. Select the "PowerTuningStateMachine" state machine.
    3. Click the "Start execution" button.
    4. Enter the input parameters for your Lambda function and the desired memory settings in JSON format, for example:
{
"lambdaARN": "arn:aws:lambda:us-east-1:123456789012:function:your-function-name",
"powerValues": [128, 256, 512, 1024, 1536, 3008],
"num": 20,
"payload": "{\\"number\\": 35}",
"parallelInvocation": true,
"strategy": "cost"
}

 

Click the "Start execution" button again, and the step function will invoke your Lambda function with the specified memory settings and gather data on execution time and cost.

Visualizing the Power Tuning Results

After the step function has completed its execution, you can visualize the results by examining the generated graph. This graph shows the relationship between the execution time and cost for each tested memory setting.

To view the graph:

  1. Navigate to the Step Functions console in the AWS Management Console.
  2. Select the Power Tuning state machine.
  3. Click on the "Optimizer" step in the completed execution.
  4. Copy the visualization URL from the "Step Output" section.
  5. Paste the URL into a new browser tab.

The graph will help you determine the best memory setting for your Lambda function, taking both cost and performance into account. You should run the step function with different memory settings to refine your understanding of the trade-offs.

lambda_performance_4

Cleanup

After completing the tutorial, cleaning up the resources created by the CloudFormation template is essential to avoid incurring ongoing costs. The cleanup process will remove all the resources associated with the AWS Lambda Power Tuning Serverless Application, including the Step Functions state machine, Lambda functions, and IAM roles.

To delete the resources, follow these steps:

  1. Navigate to the AWS CloudFormation console in the AWS Management Console.
  2. Select the stack that you created for the AWS Lambda Power Tuning Serverless Application. The stack name should start with "serverlessrepo-" followed by the application name.
  3. Click on the "Delete" button.
  4. Confirm the deletion in the pop-up window.

By performing these steps, you will remove the resources associated with the AWS Lambda Power Tuning Serverless Application and prevent further costs. Remember to review your other AWS resources, such as the Lambda functions you tested, and disable or delete them if they are no longer needed. Regularly reviewing your AWS resources and removing unnecessary ones will help you maintain a cost-effective and efficient infrastructure.

Conclusion

AWS Lambda Power Tuning is a valuable tool for optimizing your Lambda functions in terms of cost and performance. By creating a CPU-heavy Lambda function, deploying the Power Tuning Serverless Application, configuring and running the step function, and visualizing the results, you can make data-driven decisions about the memory configuration for your Lambda functions. This optimization process will enable you to strike the right balance between cost and performance, allowing you to get the most out of your serverless architecture.

Remember that every Lambda function is unique, and the best memory setting for one function may not be the best for another. Regularly reviewing and optimizing your Lambda functions using AWS Lambda Power Tuning will ensure that your serverless applications remain efficient and cost-effective over time.

 

Bonus: Optimizing Your Lambda Function with TypeScript and ESBuild

In this bonus section, you'll learn how to optimize your Lambda function further by converting the original JavaScript code to TypeScript and minifying it using ESBuild. It is important to note that since our current function isn't that large, we won't see a significant size reduction or necessarily improved performance. However, this bonus section is included to give interested readers an insight into further optimization possibilities and how to use TypeScript with AWS Lambda.

Although it's not necessary to convert the code to TypeScript first to minify it, I want to cover how to use TypeScript for Lambda functions. As of writing this, AWS Lambda only supports Node.js and doesn't support TypeScript natively. By minifying the code, you can reduce its size and potentially improve the Lambda function's performance.

There are other ways to use ESBuild, such as using it with AWS CDK, but this blog post aims to demonstrate the possibilities using the simplest approach, making it easier to follow.

Here's the original JavaScript code for the Fibonacci Lambda function, converted to TypeScript:

interface LambdaEvent {
number: number;
}

interface LambdaResponse {
statusCode: number;
body: string;
}

const fibonacci = (number: number): number => {
// Check if number is a positive integer
if (!Number.isInteger(number) || number < 0) {
// If not, throw an error with a helpful message
throw new Error('Invalid input. Please provide a positive integer.');
}

// Base case: return the number if it's 0 or 1
if (number <= 1) {
return number;
}

// Recursive case: return the sum of the previous two numbers in the sequence
return fibonacci(number - 1) + fibonacci(number - 2);
};

export const handler = async (event: LambdaEvent): Promise<LambdaResponse> => {
try {
// Extract the value of 'number' from the event object
const { number } = event;

// Call the Fibonacci function to calculate the result
const fibonacciResult = fibonacci(number);

// Return the result as a JSON object with a 200 status code
return {
statusCode: 200,
body: JSON.stringify({ fibonacciResult }),
};
} catch (error) {
// If there was an error, return a 400 status code and the error message
return {
statusCode: 400,
body: error.message,
};
}
};

 

Install ESBuild as a development dependency: In your project directory, run the following command to install ESBuild:

npm install esbuild --save-dev

 

Create a TypeScript file: Create a TypeScript file called lambda.ts in your project directory and paste the TypeScript code provided above.

Run the npx command to minify the TypeScript code: In your project directory, run the following npx command to minify the TypeScript code and generate a JavaScript file:

npx esbuild lambda.ts --minify --platform=node --target=node18 --outfile=lambda.min.js

 

This command does the following:

  • -minify: Minifies the output file by removing unnecessary characters and renaming variables.
  • -platform=node: Sets the platform to Node.js.
  • -target=node18: Specifies the target Node.js version (Node.js 18.x in this case), ensuring compatibility with the AWS Lambda runtime.
  • -outfile=lambda.min.js: Defines the output file name.

After running this command, you will have a minified JavaScript file named lambda.min.js containing the minified and bundled version of the TypeScript code. This file can be deployed as your Lambda function.

Compare Results with the Minified code

Now that you have successfully minified your Lambda function, it's time to update your function with the lambda.min.js code and compare the results.

lambda_size_before_and_after

In the image above, you can see the difference in size between the original Lambda function and the minified version. The original function was 624.0 bytes, while the minified function is now only 586.0 bytes. Admittedly, the 6.09% reduction in size may not seem significant, but it's important to remember that our Lambda function was quite small to begin with.

For larger Lambda functions, employing optimization techniques like this can have a more substantial impact on reducing the function size depending on the complexity and length of the code. Smaller function sizes can lead to faster cold starts and potentially better performance as unnecessary code is removed during the minification process. 

Using the same steps you took before Invoke your Lambda function using the AWS Lambda Power Tuning, use same test event and configuration as before. Take note of the execution time, memory consumption, and any other relevant performance metrics.

Compare the results of the minified Lambda function to the original version. Pay close attention to any improvements in cold start times, overall execution time, and memory usage.

lambda_performance_compared

In the performance comparison above, you can see that even though the minified Lambda function has a relatively small 6.09% reduction in file size, it still made a noticeable 4% difference in cost and execution time compared to the original version. This demonstrates that even minor optimizations can yield cost savings and performance improvements.

For larger Lambda functions, employing optimization techniques like this can have an even more substantial impact on reducing the function size, depending on the complexity and length of the code. Smaller function sizes can lead to faster cold starts and better performance as unnecessary code is removed during the minification process. In addition, reducing execution time and memory usage can contribute to lower costs when running your Lambda functions.

By following this bonus section, you have learned how to optimize your AWS Lambda function using TypeScript and ESBuild and gained insight into using TypeScript for Lambda functions, which AWS Lambda doesn't natively support. You have also explored the potential benefits of these techniques when applied to larger Lambda functions.