前回、C#におけるループステートメントの速度検証をまとめてみました。検証方法が未熟な箇所が多々あったため、今回は@Midoliyさんにご紹介いただきましたBenchmarkDotNet
を用いて検証を実施します。
実施すること
- 1億個の要素を持った配列およびリストを用意する。
- 準備した配列、リストに対して検証する。
- 単純にぐるぐる回す。
- ぐるぐる回しながら2の倍数を足し込んでいく。
対象のループステートメント
- for
- foreach
- do
- do-while
- LINQ(Query形式):足し込み検証のみ実施
- LINQ(Method形式):足し込み検証のみ実施
結果
単純にぐるぐる回す(配列)
Method |
Mean |
Error |
StdDev |
Median |
BenchFor_Array |
29.08 ms |
0.1590 ms |
0.1410 ms |
29.05 ms |
BenchForEach_Array |
48.22 ms |
0.0428 ms |
0.0334 ms |
48.22 ms |
BenchDo_Array |
28.93 ms |
0.0829 ms |
0.0735 ms |
28.93 ms |
BenchDoWhile_Array |
36.09 ms |
2.6406 ms |
7.7859 ms |
33.17 ms |
単純にぐるぐる回す(リスト)
Method |
Mean |
Error |
StdDev |
Median |
BenchFor_List |
36.53 ms |
0.4746 ms |
0.4439 ms |
36.40 ms |
BenchForEach_List |
248.81 ms |
10.3583 ms |
12.3308 ms |
244.77 ms |
BenchDo_List |
36.49 ms |
0.4889 ms |
0.4573 ms |
36.30 ms |
BenchDoWhile_List |
36.19 ms |
0.1416 ms |
0.1182 ms |
36.19 ms |
※配列とリストでforeachの速度がなぜここまで変わるのかは、@Tokeiyaさんの配列に対するforとforeachが非常にわかりやすかったです。
2の倍数の足し込み(配列)
Method |
Mean |
Error |
StdDev |
Median |
BenchForWithCalc_Array |
151.77 ms |
0.3014 ms |
0.2819 ms |
151.79 ms |
BenchForEachWithCalc_Array |
136.51 ms |
0.1996 ms |
0.1867 ms |
136.46 ms |
BenchDoWithCalc_Array |
151.72 ms |
0.2464 ms |
0.2305 ms |
151.65 ms |
BenchDoWhileWithCalc_Array |
151.03 ms |
1.1535 ms |
1.0225 ms |
150.97 ms |
BenchLINQQuery_Array |
611.51 ms |
5.0230 ms |
4.6985 ms |
613.74 ms |
BenchLINQMethod_Array |
589.09 ms |
11.6493 ms |
11.4412 ms |
586.88 ms |
2の倍数の足し込み(リスト)
Method |
Mean |
Error |
StdDev |
Median |
BenchForWithCalc_List |
169.25 ms |
0.3809 ms |
0.3563 ms |
169.37 ms |
BenchForEachWithCalc_List |
299.09 ms |
3.3102 ms |
3.0963 ms |
300.62 ms |
BenchDoWithCalc_List |
172.82 ms |
2.8077 ms |
2.1921 ms |
173.18 ms |
BenchDoWhileWithCalc_List |
169.38 ms |
0.5669 ms |
0.4734 ms |
169.22 ms |
BenchLINQQuery_List |
814.73 ms |
5.5605 ms |
5.2013 ms |
816.72 ms |
BenchLINQMethod_List |
825.85 ms |
7.5451 ms |
6.6885 ms |
825.91 ms |
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Median : Value separating the higher half of all measurements (50th percentile)
検証ソース
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Linq;
namespace Benchmarks
{
public class LoopBenchmarkTest
{
private static readonly int listCnt = 100_000_000; // 要素数
private static readonly int[] numArray; // 検証配列
private static readonly List<int> numList; // 検証リスト
static LoopBenchmarkTest()
{
numArray = Enumerable.Range(0, listCnt).ToArray();
numList = new List<int>(numArray);
}
#region Array
[Benchmark]
public long BenchFor_Array()
{
for (var i = 0; i <= numArray.Length - 1; i++) {}
return 0;
}
[Benchmark]
public long BenchForEach_Array()
{
foreach (var num in numArray) {}
return 0;
}
[Benchmark]
public long BenchDo_Array()
{
var i = 0;
while (i <= numArray.Length - 1)
{
i++;
}
return 0;
}
[Benchmark]
public long BenchDoWhile_Array()
{
var i = 0;
do
{
i++;
}
while (i <= numArray.Length - 1);
return 0;
}
#endregion
#region List
[Benchmark]
public long BenchFor_List()
{
for (var i = 0; i <= numList.Count - 1; i++) {}
return 0;
}
[Benchmark]
public long BenchForEach_List()
{
foreach (var num in numList) {}
return 0;
}
[Benchmark]
public long BenchDo_List()
{
var i = 0;
while (i <= numList.Count - 1)
{
i++;
}
return 0;
}
[Benchmark]
public long BenchDoWhile_List()
{
var i = 0;
do
{
i++;
}
while (i <= numList.Count - 1);
return 0;
}
#endregion
#region ArrayWithCalc
[Benchmark]
public long BenchForWithCalc_Array()
{
var total = 0;
for (var i = 0; i <= numArray.Length - 1; i++)
{
if (i % 2 == 0)
total += numArray[i];
}
return total;
}
[Benchmark]
public long BenchForEachWithCalc_Array()
{
var total = 0;
foreach (var num in numArray)
{
if (num % 2 == 0)
total += num;
}
return total;
}
[Benchmark]
public long BenchDoWithCalc_Array()
{
var i = 0;
var total = 0;
while (i <= numArray.Length - 1)
{
if (i % 2 == 0)
total += numArray[i];
i++;
}
return total;
}
[Benchmark]
public long BenchDoWhileWithCalc_Array()
{
var i = 0;
var total = 0;
do
{
if (i % 2 == 0)
total += numArray[i];
i++;
}
while (i <= numArray.Length - 1);
return total;
}
[Benchmark]
public long BenchLINQQuery_Array()
{
var total = (
from x in numArray
where x % 2 == 0
select (long)x
).Sum();
return total;
}
[Benchmark]
public long BenchLINQMethod_Array()
{
var total = numArray
.Where(x => x % 2 == 0)
.Sum(x => (long)x);
return total;
}
#endregion
#region ListWithCalc
[Benchmark]
public long BenchForWithCalc_List()
{
var total = 0;
for (var i = 0; i <= numList.Count - 1; i++)
{
if (i % 2 == 0)
total += numList[i];
}
return total;
}
[Benchmark]
public long BenchForEachWithCalc_List()
{
var total = 0;
foreach (var num in numList)
{
if (num % 2 == 0)
total += num;
}
return total;
}
[Benchmark]
public long BenchDoWithCalc_List()
{
var i = 0;
var total = 0;
while (i <= numList.Count - 1)
{
if (i % 2 == 0)
total += numList[i];
i++;
}
return total;
}
[Benchmark]
public long BenchDoWhileWithCalc_List()
{
var i = 0;
var total = 0;
do
{
if (i % 2 == 0)
total += numList[i];
i++;
}
while (i <= numList.Count - 1);
return total;
}
[Benchmark]
public long BenchLINQQuery_List()
{
var total = (
from x in numList
where x % 2 == 0
select (long)x
).Sum();
return total;
}
[Benchmark]
public long BenchLINQMethod_List()
{
var total = numList
.Where(x => x % 2 == 0)
.Sum(x => (long)x);
return total;
}
#endregion
}
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<LoopBenchmarkTest>();
}
}
}