Pros and Cons of enabling ReadyToRun with Azure Functions
Tags: azure, functions, serverless, dotnet
Published: 2020-10-04 17:48:13

In the last article, I mentioned that ReadyToRun is only available for `win-x64`. However, due to an SDK fix made a few days ago, it is now available for `win-x86` as well.

{% link shibayan/a-new-era-of-azure-functions-development-6f2 %}

The response to this article was loud, with some pointing out, among other things, that there is no evidence of performance improvement with respect to ReadyToRun.

The official documentation states the following.

>You can compile your function app as ReadyToRun binaries. ReadyToRun is a form of ahead-of-time compilation that can improve startup performance to help reduce the impact of cold-start when running in a Consumption plan.

>ReadyToRun is available in .NET 3.0 and requires version 3.0 of the Azure Functions runtime.

https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library#readytorun

I had seen the effect of ReadyToRun in a simple piece of code beforehand, but I'm going to share some measurements that I tried in my environment in a more realistic implementation.

## Enabling ReadyToRun (R2R)

Currently, the latest version (`3.0.9`) of the Azure Functions SDK cannot perform AOT compilation of the ReadyToRun assembly for the `win-x86` runtime identifier.

https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator/

This issue has been fixed in version `1.2.0` of the `ExtensionsMetadataGenerator` and can be resolved by installing it separately.

If you are deploying to Azure Functions with Visual Studio, it is recommended to add a ReadyToRun setting to your `pubxml` file.

```xml
<Project>
  <PropertyGroup>
    <!-- Make sure it matches the Azure Functions platform -->
    <RuntimeIdentifier>win-x86</RuntimeIdentifier>
    <PublishReadyToRun>true</PublishReadyToRun>
  </PropertyGroup>
</Project>
```

If you have already created a CI/CD pipeline, use the dotnet cli option to enable ReadyToRun.

```sh
# Using dotnet cli is more convenient for GitHub Actions and etc
dotnet publish -c Release -r win-x86 -p:PublishReadyToRun=true
```

Since the Consumption plan is limited to 1 core and 1.5GB memory, it is best to run the runtime in 32bit.

## Measuring environment

I created the following environment and used Webtest from Application Insights to reproduce the cold start.

- Azure Functions Runtime: v3.0.14492
- Location: East US 2
- Instance Type: Consumption
- Deploy Mode: Run From Package
- OS: Windows
- Platform: 32bit (`win-x86`)
- Test Functions: Using Durable Functions v2.3.0 and DI
- Other: Same resource group and webspace

Azure Functions have varying cold start times for several factors other than the implementation of the application, such as instance assignment by the system.

Because cold start times vary depending on the Azure region, accurate measurement is difficult in some respects, but this time we have unified the environment as much as possible.

## Compare results

I used Azure Monitor to graph the data collected by each of the Application Insights.

In this case, I looked at the time it took for the HTTP response and the time it took to start up Function Host.

### HTTP response time

![Availability results](https://dev-to-uploads.s3.amazonaws.com/i/ldstl27r8fl9zhwjyway.png)

### Host startup time

![Host startup results](https://dev-to-uploads.s3.amazonaws.com/i/ml56wa5ngmw6iz0b5bjh.png)

There are some outliers, but basically you can see that cold starts are faster with ReadyToRun enabled.

Because the difference by the instance of Function App cannot be completely eliminated by this result alone, the following results were confirmed by replacing the settings of ReadyToRun.

![Reversed R2R settings](https://dev-to-uploads.s3.amazonaws.com/i/icae0oac1mzdyodpmv24.png) 

It can be seen that the trend of the graph is reversed after 3:00 AM, when the replacement was made.

These results indicate that ReadyToRun is effective in speeding up the cold start time.

## Pros

- In a good case, the cold start is about ~20% faster
  - The start-up time of function host is about ~35% faster
- Even greater effect can be expected for large scale Functions App
  - It is not so effective in Function App with few assemblies

## Cons

- Some additional build time is required to AOT compile
- Assembly size increases after AOT compile
  - To include both IL and native code
- When the platform of Function App is changed, it is necessary to rebuild

## Conclusion

While the advantages of enabling ReadyToRun exist, there are certainly some disadvantages that come from AOT compile.

It is my personal opinion that the Consumption plan or the Premium plan with pre-warming disabled will be more effective.

Enjoy your Azure Serverless life!