19
11

C# + LINQ の速度比較(.NET Framework 4.7 ~ .NET 8)

Last updated at Posted at 2024-08-28

はじめに

当初、本記事は2件のフレームワークを比較していたため、タイトル後半部を (.NET Framework 4.8 vs .NET 8) としていましたが、その後にフレームワークを増やして6件にしたため、(.NET Framework 4.7 ~ .NET 8) と改めました

C# を使った開発では LINQ を多用します。アプリケーションのパフォーマンスについて議論する際はこの LINQ の影響が大きいと考え、フレームワークによりどれほど違いがあるのかを確かめてみました。

測定したメソッド

下記の5件の実行時間を測定しました。

  1. Select
  2. Where
  3. OrderBy
  4. Max
  5. 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 系を選択する理由はないように思います。

19
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
11