はじめに
本記事は、これまでの (.NET Framework 4.7 ~ .NET 8) の内容に .NET 10 を加え、改めてベンチマークをとり、編集しました
C# を使った開発では LINQ を多用します。アプリケーションのパフォーマンスについて議論する際はこの LINQ の影響が大きいと考え、フレームワークによりどれほど違いがあるのかを確かめてみました。
測定したメソッド
下記の5件の実行時間を測定しました。
SelectWhereOrderByMaxSum
比較したフレームワーク
下記6件のフレームワークを比較しました。
.NET Framework
| リリース | サポート終了 | |
|---|---|---|
| 4.7.2 | 2018-04-30 | ??? |
| 4.8 | 2019-04-18 | ??? |
| 4.8.1 | 2022-08-09 | ??? |
.NET
| リリース | サポート終了 | |
|---|---|---|
| 8.0.22 (LTS) | 2025-11-11 | 2026-11-10 |
| 9.0.11 | 2025-11-11 | 2026-11-10 |
| 10.0.0 (LTS) | 2025-11-11 | 2028-11-14 |
.NET Framework は 4.8.x が最後のリリースです。また、このサポート終了は Windows Server 2022 のサポート期限を考慮すると「2031-10-14 より後」と考えられているようです。
対して .NET 10 のサポート終了は「2028-11-14」と .NET Framework 4.8 より早くサポート終了を迎えますが、後続として LTS 版である .NET 12 のリリースが控えています。
.NET 12 は 2027年11月にリリースされる予定であり、リリースから 3年間のサポートが予定されています。
検証環境
下記スペックの PC にて検証しました。
- CPU: Intel(R) Core(TM) Ultra 7 155H (3.80 GHz)
- RAM: 32.0 GB
- OS: Windows 11 Pro (25H2 / 26200.7171)
検証方法
Microsoft Visual Studio Professional 2026 Version 18.0.0 にて検証ソースを Release ビルドして実行しました。
検証結果
.NET 10 が比較したメソッドの全てにおいて速いです。
特に Sum メソッドが圧倒的に高速化 されています。
※ Excel 帳票を出力する際に Sum メソッドを多用する私にはとても嬉しい結果です 😍
下記の表は .NET Framework 4.8.1 での結果を 1.000 とした場合の値です
所要時間
| 4.7.2 | 4.8 | 4.8.1 | |
|---|---|---|---|
| Select | 1.094 | 0.970 | 1.000 |
| Where | 1.046 | 1.105 | 1.000 |
| OrderBy | 1.033 | 1.091 | 1.000 |
| Max | 1.020 | 1.091 | 1.000 |
| Sum | 1.037 | 1.082 | 1.000 |
| 8.0.22 | 9.0.11 | 10.0.0 |
|---|---|---|
| 0.231 | 0.212 | 0.191 |
| 0.539 | 0.426 | 0.512 |
| 0.535 | 0.498 | 0.477 |
| 0.676 | 0.596 | 0.216 |
| 0.780 | 0.699 | 0.135 |
メモリ使用量
| 4.7.2 | 4.8 | 4.8.1 | |
|---|---|---|---|
| Select | 1.000 | 1.000 | 1.000 |
| Where | 1.000 | 1.000 | 1.000 |
| OrderBy | 1.000 | 1.000 | 1.000 |
| Max | 1.000 | 1.000 | 1.000 |
| Sum | 1.000 | 1.000 | 1.000 |
| 8.0.22 | 9.0.11 | 10.0.0 |
|---|---|---|
| 0.958 | 0.958 | 0.958 |
| 1.997 | 0.610 | 0.610 |
| 0.955 | 0.955 | 0.955 |
| 0.833 | 0.833 | 0.833 |
| 0.833 | 0.833 | - |
検証ソース
下記のコードにて BenchmarkDotNet を利用し、処理の所要時間とメモリ使用量を出力しました。
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqBenchmark
{
internal class Program
{
/// <summary>
/// エントリポイント
/// </summary>
private static void Main()
{
// ベンチマークスタート
BenchmarkRunner.Run<LinqOperation>();
Console.ReadLine();
}
}
[MemoryDiagnoser]
public class LinqOperation
{
/// <summary>リストの要素数</summary>
private const int LEN = 10000;
/// <summary>リスト</summary>
private readonly List<Entity> _entities = new List<Entity>();
/// <summary>
/// コンストラクタ
/// </summary>
public LinqOperation()
{
// リストを初期化
for (var i = 0; i < LEN; i++) _entities.Add(new Entity(i));
// シャッフル
_entities = _entities.OrderBy(n => n.Guid).ToList();
}
[Benchmark]
public void Select()
{
var array = _entities.Select(n => n.No.ToString()).ToArray();
if (array.Length != LEN) throw new Exception();
}
[Benchmark]
public void Where()
{
var list = _entities.Where(n => n.No % 2 == 0).ToList();
if (list.Count != LEN / 2) throw new Exception();
}
[Benchmark]
public void OrderBy()
{
var list = _entities.OrderBy(n => n.No).ToList();
if (list[0].No != 1 || list[LEN - 1].No != LEN) throw new Exception();
}
[Benchmark]
public void Max()
{
var max = _entities.Max(n => n.No);
if (max != LEN) throw new Exception();
}
[Benchmark]
public void Sum()
{
long sum = _entities.Sum(n => n.No);
if (sum != 50005000) throw new Exception();
}
}
internal class Entity
{
public int No { get; set; }
public string Guid { get; set; }
public Entity(int index)
{
No = index + 1;
Guid = System.Guid.NewGuid().ToString();
}
}
}
測定データ
.NET Framework 4.7.2
| Method | Mean | Allocated |
|---|---|---|
| Select | 486.46 us | 407,658 B |
| Where | 127.37 us | 65,810 B |
| OrderBy | 1,338.41 us | 251,624 B |
| Max | 67.51 us | 48 B |
| Sum | 62.69 us | 48 B |
.NET Framework 4.8
| Method | Mean | Allocated |
|---|---|---|
| Select | 431.27 us | 407,657 B |
| Where | 134.59 us | 65,810 B |
| OrderBy | 1,413.95 us | 251,624 B |
| Max | 72.23 us | 48 B |
| Sum | 65.40 us | 48 B |
.NET Framework 4.8.1
| Method | Mean | Allocated |
|---|---|---|
| Select | 444.65 us | 407,657 B |
| Where | 121.79 us | 65,810 B |
| OrderBy | 1,295.86 us | 251,624 B |
| Max | 66.21 us | 48 B |
| Sum | 60.46 us | 48 B |
.NET 8.0.22
| Method | Mean | Allocated |
|---|---|---|
| Select | 102.81 us | 390,528 B |
| Where | 65.59 us | 131,432 B |
| OrderBy | 692.68 us | 240,304 B |
| Max | 44.79 us | 40 B |
| Sum | 47.17 us | 40 B |
.NET 9.0.11
| Method | Mean | Allocated |
|---|---|---|
| Select | 94.17 us | 390,528 B |
| Where | 51.85 us | 40,128 B |
| OrderBy | 644.80 us | 240,336 B |
| Max | 39.45 us | 40 B |
| Sum | 42.26 us | 40 B |
.NET 10.0.0
| Method | Mean | Allocated |
|---|---|---|
| Select | 85.067 us | 390,528 B |
| Where | 62.362 us | 40,128 B |
| OrderBy | 617.540 us | 240,336 B |
| Max | 14.333 us | 40 B |
| Sum | 8.180 us | - |
凡例
-
Mean: Arithmetic mean of all measurements -
Error: Half of 99.9% confidence interval -
StdDev: Standard deviation of all measurements -
Allocated: Allocated memory per single operation (managed only, inclusive, 1KB = 1,024B) -
1 us: 1 Microsecond (0.000001 sec)
おわりに
検証した LINQ メソッドの全てにおいて速くメモリ使用量も少なく済む結果となった .NET 10 は、後続の .NET 11、.NET 12 にてさらなる速度改善が期待されています。パフォーマンス面では、今後の開発において .NET Framework 系を選択する理由はないように思います。