前回、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>();
}
}
}