Повышение производительности в .NET 10
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 07.10.25 09:32
Оценка: 95 (3)
Performance Improvements in .NET 10

На русском

JIT
Среди всех областей .NET наиболее значимым является JIT-компилятор (Just-In-Time, «точно в срок»). Каждое приложение .NET, будь то небольшой консольный инструмент или крупномасштабная корпоративная служба, в конечном счёте использует JIT-компилятор для преобразования кода на промежуточном языке (IL) в оптимизированный машинный код. Любое улучшение качества кода, генерируемого JIT-компилятором, оказывает комплексное воздействие, повышая производительность всей экосистемы без необходимости вносить изменения в собственный код или даже перекомпилировать C#. И в .NET 10 таких улучшений предостаточно.

Деабстракция
Как и во многих других языках, в .NET исторически наблюдался «штраф за абстракцию» — дополнительные выделения памяти и косвенная адресация, которые могут возникать при использовании высокоуровневых языковых функций, таких как интерфейсы, итераторы и делегаты. С каждым годом JIT-компилятор всё лучше оптимизирует уровни абстракции, позволяя разработчикам писать простой код и при этом получать высокую производительность. .NET 10 продолжает эту традицию. В результате идиоматический C# (с использованием интерфейсов, циклов foreach , лямбда-выражений и т. д.) работает почти так же быстро, как тщательно продуманный и отлаженный вручную код.

Выделение стека объектов
Одним из самых интересных направлений в области деабстракции в .NET 10 является расширенное использование анализа выхода за пределы метода для выделения объектов в стеке. Анализ выхода за пределы метода — это метод компилятора, который позволяет определить, выходит ли объект, выделенный в методе, за пределы этого метода, то есть доступен ли этот объект после возврата из метода (например, сохранён ли он в поле или возвращён вызывающей стороне) или используется каким-то образом, который среда выполнения не может отследить в рамках метода (например, передан неизвестному вызываемому объекту). Если компилятор может доказать, что объект не выйдет за пределы метода, то время жизни этого объекта будет ограничено методом, и его можно будет выделить в стеке, а не в куче. Выделение в стеке обходится гораздо дешевле (простое увеличение указателя для выделения и автоматическое освобождение при выходе из метода) и снижает нагрузку на сборщик мусора, поскольку объект не нужно отслеживать сборщику мусора. В .NET 9 уже была реализована ограниченная поддержка анализа выхода за пределы метода и выделения в стеке; в .NET 10 эта поддержка значительно расширена.


public partial class Tests
{
    [Benchmark]
    [Arguments(42)]
    public int Sum(int y)
    {
        Func<int, int> addY = x => x + y;
        return DoubleResult(addY, y);
    }

    private int DoubleResult(Func<int, int> func, int arg)
    {
        int result = func(arg);
        return result + result;
    }
}


If we just run this benchmark and compare .NET 9 and .NET 10, we can immediately tell something interesting is happening.

Method Runtime Mean Ratio Code Size Allocated Alloc Ratio
Sum .NET 9.0 19.530 ns 1.00 118 B 88 B 1.00
Sum .NET 10.0 6.685 ns 0.34 32 B 24 B 0.27

и солнце б утром не вставало, когда бы не было меня
Отредактировано 09.10.2025 12:25 VladD2 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.