LoginSignup
1

More than 3 years have passed since last update.

【C#】ループステートメントの速度を検証してみた その2

Last updated at Posted at 2020-09-01

前回、C#におけるループステートメントの速度検証をまとめてみました。検証方法が未熟な箇所が多々あったため、今回は@Midoliyさんにご紹介いただきましたBenchmarkDotNetを用いて検証を実施します。

実施すること

  1. 1億個の要素を持った配列およびリストを用意する。
  2. 準備した配列、リストに対して検証する。
    1. 単純にぐるぐる回す。
    2. ぐるぐる回しながら2の倍数を足し込んでいく。

対象のループステートメント

  1. for
  2. foreach
  3. do
  4. do-while
  5. LINQ(Query形式):足し込み検証のみ実施
  6. 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>();
        }
    }
}

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
What you can do with signing up
1