Существует множество опций, влияющих на производительность. На самом деле, я нашел это немного подавляющим, поэтому я хотел проверить все возможные комбинации.
Параметры компилятора C#
Многоуровневая компиляция
Это опция среды выполнения .NET, которую можно установить во время компиляции. Она инструктирует среду выполнения .NET выполнять грязную JIT (называемую Tier0), которая генерируется быстрее, но приводит к менее производительному коду. Если код выполняется достаточно часто, то позже он будет заменен оптимизированной версией (называемой Tier1).
Без многоуровневой компиляции джиттер генерирует весь код как Tier1. Оптимизация начального кода может быть расточительной, особенно если код выполняется только один раз. При включенной опции джиттер ждет 100 мс, прежде чем начать оптимизацию методов, которые вызываются 30 или более раз. Это означает, что экономия времени, полученная во время холодного запуска Lambda, влияет на последующие теплые вызовы.
Весь процесс довольно сложный и увлекательный. Для получения более подробной информации ознакомьтесь со спецификацией Tiered Compilation.
ReadyToRun
Эта опция компилятора .NET дает указание компилятору включить в создаваемую сборку предварительно откопированный код. Обратите внимание, что эта опция может увеличить размер сборки на 200-300%.
Во время запуска среда выполнения использует предварительно созданный код, но только если он соответствует архитектуре процессора среды выполнения. Предварительно собранный код не оптимизирован и эквивалентен коду грязного JIT (Tier0). Если также включена многоуровневая компиляция, то при достаточно частом обращении к коду предварительной выборки он в конечном итоге оптимизируется.
Более подробную информацию можно найти на официальной странице о ReadyToRun Compilation.
Параметры лямбда-функции
Память
Производительность среды выполнения лямбда-функций напрямую связана с конфигурацией памяти. Однако эта связь не является линейной. Однопоточная производительность достигает максимума при 3 008 МБ, что на 100% обеспечивает производительность 2 ядер vCPU. После этого добавляются дополнительные дробные ядра, пока не будет достигнут максимум в 10 240 МБ, что обеспечивает 6 ядер.
Глубокий анализ конфигурации памяти Lambda и ее влияния на производительность можно найти в статье Оптимизация стоимости Lambda с помощью многопоточности.
Важной деталью является то, что производительность повышается на этапе INIT среды выполнения. Нет разницы, настроена ли функция Lambda на 128 МБ или на 3 008 МБ. В обоих случаях продолжительность фазы INIT будет одинаковой и производительность такой же, как если бы Lambda была настроена на 3 008 МБ. Только при превышении этого порога фаза INIT будет выполняться быстрее, предполагая, что она может использовать более двух ядер.
Архитектура ЦП
AWS Lambda поддерживает две архитектуры процессора, в зависимости от региона: x86 64-бит и ARM64. Стоимость ARM64 на 20% ниже, чем x86 при одинаковой конфигурации памяти. Это делает его очень привлекательным выбором, когда он доступен.
На момент написания этой статьи следующие регионы еще не поддерживают ARM64 для Lambda.
- Запад США — Северная Калифорния
- Африка — Кейптаун
- Азиатско-Тихоокеанский регион — Гонконг, Джакарта, Осака и Сеул
- Канада — Центральный
- Европа — Милан, Париж и Стокгольм
- Ближний Восток — Бахрейн
- Южная Америка — Сан-Паулу
- AWS GovCloud — США-Восток и США-Запад
Время выполнения .NET
В настоящее время только .NET Core 3.1 и .NET 6 runtimes могут использоваться для развертывания новых функций. Однако, в типичной моде AWS, старые функции продолжают работать. Я могу подтвердить это, поскольку у меня до сих пор работают некоторые старые функции .NET Core 1.0.
Имейте в виду, что .NET Core 3.1 выйдет из эксплуатации 13 декабря 2022 года. В какой-то момент после этого создание новых функций .NET Core 3.1 станет невозможным.
Хост .NET для AWS Lambda
Pre-JIT .NET — AWS_LAMBDA_DOTNET_PREJIT
.
Когда эта переменная окружения имеет значение «Всегда», она инструктирует .NET-хост для AWS Lambda подготавливать код во время фазы INIT среды выполнения, а не ждать фазы INVOKE.
По умолчанию он используется для Provisioned Concurrency, который позволяет предварительно инициализировать одну или несколько сред выполнения Lambda, чтобы избежать холодного старта. Однако ее также можно установить так, чтобы она всегда выполняла подготовку кода.
Интересное свойство этой переменной окружения заключается в том, что она переносит часть накладных расходов на подготовку кода (code jitting) с фазы INVOKE на фазу INIT. Фаза INIT всегда выполняется на уровне производительности конфигурации с 3 008 МБ памяти, если не задано больше. Кроме того, фаза INIT является бесплатной, если ее длительность не превышает 10 секунд.
Что дальше
В следующем посте я расскажу о методологии проведения бенчмарков.