Мне всегда нравится понимать, как выглядит нижняя граница. Какова самая быстрая производительность, на которую мы можем надеяться? Я нахожу его проницательным, поскольку он устанавливает основу для всего остального.

Необходимое предупреждение здесь — риск экстраполяции слишком многого из такой тривиальной выборки. Нам нужно принимать данные такими, какие они есть: базовыми. Это не представитель реальной бизнес-логики. Простое добавление некоторых операций ввода-вывода значительно увеличило бы время обработки. Обычно ввод-вывод в 1000-1000000 раз медленнее, чем код.


Минимальная лямбда-функция

Минимальный проект определяет функцию Lambda, которая принимает поток и возвращает пустой ответ. Он не имеет бизнес-логики и включает только необходимые библиотеки. Также отсутствует десериализация полезной нагрузки. Это лямбда-функция с наименьшим количеством накладных расходов.

using System.IO;
using System.Threading.Tasks;

namespace Benchmark.Minimal {

    public sealed class Function {

        //--- Methods ---
        public async Task<Stream> ProcessAsync(Stream request)
            => Stream.Null;
    }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима


Сравнительные данные для .NET 6 на платформе x86-64

Данные четко показывают, что фаза INIT примерно одинакова для всех конфигураций памяти ниже порога 3008 МБ. Как упоминалось в статье «Анатомия жизненного цикла AWS Lambda», этап INIT всегда выполняется на полной скорости.

Холодная фаза INVOKE примерно в 10 раз медленнее для 128 МБ, чем для 1024 МБ. Однако сумма всех теплых фаз INVOKE только примерно в 3 раза медленнее. Тем не менее, стоимость улучшенной производительности меньше чем на 5%.

Удивительно, что даже на таком тривиальном примере мы уже можем оценить тонкий баланс между производительностью и стоимостью.

Объем памятиВ этомХолодное использованиеОбщий холодный пускОбщее количество использованного тепла (100)Стоимость (мк$)
128 МБ235.615620.921856,536365,51922.25509
256 МБ238.296315.731554.027150,12422.14107
512 МБ241,193136,89378.083124.68622.37980
1024 МБ239,97260.804300.776115,5323.13891
1769 МБ241.00537,623278,628116.32224.63246
5120 МБ218.11237.009255,121119,55933.24730

Продолжительность холодного пуска

Полноразмерное изображение

Затраты на выполнение за весь срок службы и общее время теплого выполнения

Полноразмерное изображение


Минимальная продолжительность холодного запуска для .NET 6

Неудивительно, что наименьшая продолжительность холодного запуска была достигнута при использовании самой высокой конфигурации памяти. Многоуровневая компиляция также помогло снизить число. Однако, Готов к запуску не оказало большого влияния, чего и следовало ожидать, поскольку в нашем минимальном проекте почти нет кода.

Более примечательно то, что архитектура ARM64 была медленнее для сопоставимых конфигураций памяти, чем архитектура x86-64.

АрхитектураОбъем памятиМногоуровневыйReady2RunPreJITВ этомХолодное использованиеОбщий холодный пуск
рука645120 МБданетнет211.00630.165241,171
x86_641024 МБданетнет213.08533.173246,258
x86_641769 МБданетнет215.75424.164239,918
x86_645120 МБданетнет198.77124.094222,865

Минимальная продолжительность холодного запуска для .NET 6

Полноразмерное изображение


Минимальная стоимость выполнения для .NET 6

Другой неудивительный результат заключается в том, что архитектура ARM64 обеспечивает наименьшую стоимость исполнения, поскольку ее цена за единицу ниже на 20%. Точно так же конфигурация памяти ближе к нижнему пределу — всего 256 МБ.

Более интересно то, что Многоуровневая компиляция всегда дороже в эксплуатации. Это имеет интуитивно понятный смысл, поскольку требуется дополнительное время обработки для перекомпоновки кода. После этого, это немного жеребьевки между Готов к запуску а также PreJIT настройки.

АрхитектураОбъем памятиМногоуровневыйReady2RunPreJITВ этомХолодное использованиеОбщее количество использованного тепла (100)Стоимость (мк$)
рука64256 МБнетнетнет266.026378,676158.06421.98914228
рука64256 МБнетнетда288.025371,274161,52921.97601788
рука64256 МБнетданет264.304361,657164,61921.95426344
рука64256 МБнетдада287,762361,285160,24821.93844936

Стоимость выполнения за весь срок службы и общее время выполнения в горячем режиме для .NET 6

Полноразмерное изображение


А как насчет .NET Core 3.1?

Мне было трудно упомянуть об этом, поскольку срок службы .NET Core 3.1 подходит к концу в декабре 2022 года, но разница в производительности для базового случая просто ошеломляет.

Функция Lambda, использующая .NET Core 3.1 с 512 МБ, на 40 % быстрее при холодном запуске, чем функция, использующая .NET 6 с 5120 МБ!

Я просто в шоке от такого результата. Все, что я могу сделать, это напомнить себе, что этот базовый тест не отражает реальный код.

АрхитектураОбъем памятиМногоуровневыйReady2RunPreJITВ этомХолодное использованиеОбщий холодный пуск
x86_64512 МБданетнет150,1296.903157.032
x86_641024 МБданетнет148.3766.081154.457
x86_641769 МБданетнет148.3385.972154,31

Минимальная продолжительность холодного запуска для .NET Core 3.1

Полноразмерное изображение

Точно так же стоимость выполнения ниже с .NET Core 3.1, но не так значительно. Тем не менее, для .NET 6 было всего 4 конфигурации, стоимость которых не превышала 22 мк$. Для .NET Core 3.1 доступно 39 конфигураций по цене менее 21 µ$!

Интересно, что 4 самые дешевые конфигурации следуют аналогичной схеме: ARM64, 128 МБ, нет Многоуровневая компиляцияи жеребьевка для Готов к запуску а также PreJIT.

АрхитектураОбъем памятиМногоуровневыйReady2RunPreJITВ этомХолодное использованиеОбщее количество использованного тепла (100)Стоимость (мк$)
рука64128 МБнетнетнет162.366102.693110.09620.55465044
рука64128 МБнетнетда186,62798,641112.32720.55161642
рука64128 МБнетданет161,98988,677110.39120.53178133
рука64128 МБнетдада185,92385,289117,81120.53850086

Стоимость выполнения за весь срок службы и общее время выполнения в горячем режиме для .NET Core 3.1

Полноразмерное изображение


Вывод

Основываясь на тестах, мы можем установить эти нижние границы.

Для .NET 6:

  • Продолжительность холодного старта: 223 мс
  • Стоимость исполнения: 21,94µ$

Для .NET Core 3.1:

  • Продолжительность холодного старта: 154 мс
  • Стоимость исполнения: 20,53µ$

Если ничего фундаментального не изменится, мы не должны ожидать большего, чем эти базовые значения.


Что дальше

В следующем посте я собираюсь протестировать сериализаторы JSON. В частности, популярная библиотека Newtonsoft JSON.NET, встроенная Система.Текст.Json пространство имен и новые генераторы исходного кода .NET 6 JSON.