はじめに
当初、本記事は2件のフレームワークを比較していたため、タイトル後半部を (.NET Framework 4.8 vs .NET 8)
としていましたが、その後にフレームワークを増やして6件にしたため、(.NET Framework 4.7 ~ .NET 8)
と改めました
C# を使った開発では LINQ を多用します。アプリケーションのパフォーマンスについて議論する際はこの LINQ の影響が大きいと考え、フレームワークによりどれほど違いがあるのかを確かめてみました。
測定したメソッド
下記の5件の実行時間を測定しました。
Select
Where
OrderBy
Max
Sum
比較したフレームワーク
下記6件のフレームワークを比較しました。
.NET Framework
リリース | サポート終了 | |
---|---|---|
4.7.2 | 2018-04-30 | ??? |
4.8 | 2019-04-18 | ??? |
4.8.1 | 2022-08-09 | ??? |
.NET
リリース | サポート終了 | |
---|---|---|
6.0.33 (LTS) | 2021-11-08 | 2024-11-12 |
7.0.20 | 2022-11-08 | 2024-05-14 |
8.0.8 (LTS) | 2023-11-14 | 2026-11-10 |
.NET Framework は 4.8.x が最後のリリースです。また、このサポート終了は Windows Server 2022 のサポート期限を考慮すると「2031-10-14 より後」と考えられているようです。
対して .NET 8 のサポート終了は「2026-11-10」と .NET Framework 4.8 より早くサポート終了を迎えますが、後続として LTS 版である .NET 10 のリリースが控えています。
.NET 10 は 2025年11月にリリースされる予定であり、リリースから 3年間のサポートが予定されています。
検証環境
下記スペックの PC にて検証しました。
- CPU: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz 2.30 GHz
- RAM: 32.0 GB
- OS: Windows 10 Pro (x64 / 19045.4780)
検証方法
Microsoft Visual Studio Professional 2022 (64 ビット) Version 17.10.6 にて検証ソースを Release ビルドして実行しました。
検証結果
.NET 8 が比較したメソッドの全てにおいて速いです。
メモリ使用量は概ね変わりません。Where
のみ .NET 系が倍ほど利用しています
下記の表は .NET Framework 4.8.1 での結果を 1.000
とした場合の値です
所要時間
4.7.2 | 4.8 | 4.8.1 | |
---|---|---|---|
Select | 1.516 | 1.008 | 1.000 |
Where | 1.021 | 1.002 | 1.000 |
OrderBy | 1.033 | 1.005 | 1.000 |
Max | 1.032 | 1.031 | 1.000 |
Sum | 1.018 | 1.002 | 1.000 |
6.0.33 | 7.0.20 | 8.0.8 |
---|---|---|
0.278 | 0.268 | 0.213 |
0.586 | 0.595 | 0.515 |
0.804 | 0.788 | 0.479 |
0.961 | 1.022 | 0.503 |
1.201 | 1.246 | 0.656 |
メモリ使用量
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 |
6.0.33 | 7.0.20 | 8.0.8 |
---|---|---|
0.981 | 0.981 | 0.958 |
1.997 | 1.997 | 1.997 |
0.955 | 0.955 | 0.955 |
0.816 | 0.816 | 0.816 |
0.816 | 0.816 | 0.816 |
検証ソース
下記のコードにて 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 | 1,296.70 us | 407,668 B |
Where | 203.97 us | 65,800 B |
OrderBy | 1,834.87 us | 251,608 B |
Max | 107.30 us | 49 B |
Sum | 86.47 us | 49 B |
.NET Framework 4.8
Method | Mean | Allocated |
---|---|---|
Select | 862.35 us | 407,666 B |
Where | 200.16 us | 65,800 B |
OrderBy | 1,784.52 us | 251,608 B |
Max | 107.19 us | 49 B |
Sum | 85.12 us | 49 B |
.NET Framework 4.8.1
Method | Mean | Allocated |
---|---|---|
Select | 855.14 us | 407,662 B |
Where | 199.83 us | 65,800 B |
OrderBy | 1,776.46 us | 251,608 B |
Max | 103.94 us | 49 B |
Sum | 84.93 us | 49 B |
.NET 6.0.33
Method | Mean | Allocated |
---|---|---|
Select | 238.06 us | 399,808 B |
Where | 117.12 us | 131,432 B |
OrderBy | 1,428.17 us | 240,309 B |
Max | 99.89 us | 40 B |
Sum | 101.97 us | 40 B |
.NET 7.0.20
Method | Mean | Allocated |
---|---|---|
Select | 229.5 us | 399,808 B |
Where | 118.8 us | 131,432 B |
OrderBy | 1,400.1 us | 240,305 B |
Max | 106.2 us | 40 B |
Sum | 105.8 us | 40 B |
.NET 8.0.8
Method | Mean | Allocated |
---|---|---|
Select | 182.57 us | 390,528 B |
Where | 102.87 us | 131,432 B |
OrderBy | 850.84 us | 240,304 B |
Max | 52.27 us | 40 B |
Sum | 55.74 us | 40 B |
凡例
-
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 8 は、後続の .NET 9、.NET 10 にてさらなる速度改善が期待されています。パフォーマンス面では、今後の開発において .NET Framework 系を選択する理由はないように思います。