Reducing AWS Lambda Cold Starts with NET. 7 Native AOT

Somebody who likes to code
Search for a command to run...

Somebody who likes to code
No comments yet. Be the first to comment.
Working with serverless technologies like Lambda Functions is great, but it is easy to lose control of what to deploy (due to the large number of resources we usually have to deploy). In a previous post, we explore a solution for that, Serverless Fra...
Google processes tens of thousands of code changes every day across a codebase of hundreds of millions of lines. The secret isn't magic tooling — it's a documented, principled approach to code review

Amazon Simple Notification Service (SNS) looks straightforward on the surface — publish a message, deliver it to subscribers — but the gap between a topic that "works in dev" and one that is productio

Amazon Simple Queue Service (SQS) is deceptively simple to get started with, but surprisingly easy to misconfigure in ways that cause silent message loss, runaway costs, duplicate processing storms, o

HTTP API is the right default choice for the majority of .NET serverless workloads. At 70% less cost than REST API, lower latency, and native JWT authorization, it covers most production requirements

AWS Lambda has become a cornerstone of modern serverless architectures, but writing a function that "just works" is very different from writing one that is performant, cost-effective, secure, and read

In the AWS Lambda ecosystem, the cold start problem could be a key factor to use it or not, especially if you're developing a customer-facing application that needs to operate in real-time. AWS is continuously releasing new features to minimize the impact on our applications, such as Provisioned concurrency or Lambda SnapStart (only available for Java). NET 7 is also helping to reduce the impact of the cold start problem with Native AOT. To understand it, let's check first how JIT (Just-In-Time compilation) works:
On the other hand, Native AOT produces a self-contained application that has been Ahead-Of-Time (AOT) compiled into native code at the time of publishing. That improves the performance and reduces startup time as we do not have to execute the JIT compiler when the application runs. But there are some drawbacks, such as:
Assembly.LoadFile).System.Reflection.Emit).To measure the improvements in the cold start time. We build two applications, the first one with .NET 6 and the second using .NET 7 with Native AOT (following this post). Both Lambda Function are going to do the same (the source code can be found here:
For both Lambda Functions, we are using 512 Mb. We run a test (three times, redeploying the applications between runs) to hit the endpoints for 5 minutes (one request per second). Let's take a look at the results:
| Application | Min | Avg | Max | Median | P95 | P99 | Stddev |
| Native AOT | 18ms | 29ms | 869ms | 23ms | 46ms | 86ms | 51ms |
| JIT | 17ms | 34ms | 1417ms | 23ms | 54ms | 81ms | 83ms |
| Application | Min | Avg | Max | Median | P95 | P99 | Stddev |
| Native AOT | 19ms | 28ms | 772ms | 23ms | 47ms | 79ms | 46ms |
| JIT | 18ms | 36ms | 1547ms | 24ms | 65ms | 99ms | 92ms |
| Application | Min | Avg | Max | Median | P95 | P99 | Stddev |
| Native AOT | 18ms | 29ms | 851ms | 23ms | 50ms | 91ms | 51ms |
| JIT | 20ms | 37ms | 1644ms | 25ms | 56ms | 75ms | 97ms |
The cold start time (Max) produced by .NET 7 with Native AOT is almost half of .NET 6. Another thing to notice is that the standard deviation is lower, suggesting that the response times are more stable. Checking the logs, we see another advantage of .NET 7 with Native AOT, the Lambda Function is using less memory (81Mb vs. 98Mb), and we confirm that the cold start time (354.51ms vs. 1015.76ms) is lower (almost three times from the AWS perspective):


But nothing could be perfect, and the package size of the Lambda Function produced by .NET 7 with Native AOT is almost four times larger (2.6Mb vs. 10.4Mb). Thanks, and happy coding.