.NET 10: размещение массивов value-типов на стеке
В .NET 10 JIT может размещать на стеке небольшие массивы фиксированного размера из value-типов, исключая выделение памяти в куче и обеспечивая до 60% прироста производительности по сравнению с .NET 9.
Начиная с .NET 9, JIT-компилятор стал умнее в том, как выделяет память для объектов. Если он может определить, что объект не будет использоваться после завершения метода, в котором он был создан, то может разместить его на стеке, а не в куче. Это серьёзный выигрыш в производительности, потому что сборке мусора не приходится за ним следить. Кроме того, размещение на стеке позволяет JIT применять ещё больше оптимизаций, например, заменять весь объект на его отдельные поля или значения. Это делает использование ссылочных типов значительно дешевле с точки зрения производительности.
В .NET 10 эта возможность была расширена и теперь охватывает небольшие массивы фиксированного размера из value-типов. JIT размещает такие массивы на стеке, когда знает, что они живут только в пределах метода, в котором они находятся.
Рассмотрим пример:
static void Sum()
{
int[] numbers = {1, 2, 3};
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
sum += numbers[i];
}
Console.WriteLine(sum);
}
Здесь массив numbers содержит всего три целых числа и используется только внутри метода Sum. Поскольку JIT знает его размер и область видимости на этапе компиляции, он может безопасно разместить массив на стеке. Это означает отсутствие выделения в куче и более высокую производительность.
Бенчмарки
Сравнивая .NET 9 и .NET 10, мы ясно видим, что для приведённого выше сценария в куче больше ничего не выделяется. Это также даёт довольно значительный прирост производительности: .NET 10 на 60% быстрее в измеренном сценарии.
| Method | Runtime | Mean | Ratio | Gen0 | Allocated | Alloc Ratio |
|-------------- |---------- |---------:|------:|-------:|----------:|------------:|
| AllocateArray | .NET 10.0 | 3.041 ns | 0.40 | - | - | 0.00 |
| AllocateArray | .NET 9.0 | 7.675 ns | 1.00 | 0.0067 | 56 B | 1.00 |
Если вы хотите запустить бенчмарк самостоятельно, код приведён ниже:
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90, baseline: true)]
[SimpleJob(RuntimeMoniker.Net10_0)]
[HideColumns("Job", "Error", "StdDev", "RatioSD")]
public class ArrayAllocationBenchmarks
{
[Benchmark]
public int AllocateArray()
{
int total = 0;
int[] numbers = { 1, 2, 3, 4, 5, 6, 7 };
for (int i = 0; i < numbers.Length; i++)
{
total += numbers[i];
}
return total;
}
}
internal class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<ArrayAllocationBenchmarks>();
}
}
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.