配列に入ったbyte要素が全て0でないことを比較するループの最速解を求める為にコーディングして速度比較していたときのコードのメモ

配列をループさせてチェックする際に、どうやったら速くなるかということを調べたときのメモ。項目が不適切だったり、実施内容が不適切だったりしますが、お手柔らかに。

# 環境
OS: Windows10 Pro 1703 x64
CPU: Intel🄬 Core™2 Duo CPU T7250 @ 2.00GHz
RAM: 4.00GB
.Net Framework 4.7

# チェック項目
次の項目で100回ずつ実行して平均を取得する。

1. 配列としてforeachでのチェック
2. 配列としてforでのチェック
3. IEnumerableとしてforeachでのチェック
4. Enumerable.Any でのチェック
5. Enumerable.All でのチェック
6. unchecked下で0->length方向へのチェック
7. unchecked下でlength->0方向へのチェック
8. unsafeによりbyte単位として扱ってのチェック
9. unsafeによりlong単位として扱ってのチェック

# 結果
結果としては次の通りです。

1. 1.2252321ms
2. 1.1569914ms
3. 5.6089002ms
4. 7.1614101ms
5. 7.1803457ms
6. 0.8632132ms
7. 0.8956111ms
8. 0.8691015ms
9. 0.1103982ms

# 総括
結果からみて、次の様に言えると思われます。

- 0であることのチェックであれば byte を long として扱えば効率は良いのだなーと再実感しました。(9.)
- foreachに食わせる場合も、配列とIEnumerableで効率化が図られていることを再確認しました。（1. 3.）
- LINQはIEnumerableを使う以上、大分遅い (3. 4.)
- uncheckedは多少効果はある。（6. 7. 8. 9.）
- unsafeしても効果的な方法をとらない場合はunsafeでなくても特に速度は変わらないことを確認しました。(8. 9.)

# ソース

```csharp:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace ArrayTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var bytes = new byte[512000];
            DateTime Time;

            var MAX = 1000;
            var sum = TimeSpan.Zero;

            // 配列として0チェック(foreach版)
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray)}:{sum.TotalMilliseconds / MAX}ms");

            // 配列として0チェック(for版)
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray2(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray2)}:{sum.TotalMilliseconds / MAX}ms");

            // IEnumerableとして0チェック
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkEnumerable(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkEnumerable)}:{sum.TotalMilliseconds / MAX}ms");

            // Enumerable.Anyとして0チェック
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                bytes.Any((v) => v != 0);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(Enumerable.Any)}:{sum.TotalMilliseconds / MAX}ms");

            // Enumerable.Allとして0チェック
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                bytes.All((v) => v == 0);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(Enumerable.All)}:{sum.TotalMilliseconds / MAX}ms");

            // unchecked下で forで0チェックを行う
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray3(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray3)}:{sum.TotalMilliseconds / MAX}ms");

            // uncheckを使用した上でforで0方向による0チェックを行う
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray4(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray4)}:{sum.TotalMilliseconds / MAX}ms");

            // unsafeを用いて ポインタによる0チェックを行う
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray5(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray5)}:{sum.TotalMilliseconds / MAX}ms");

            // unsafeを用いて long間隔で0チェックを行う
            sum = TimeSpan.Zero;
            Time = DateTime.Now;
            for (var i = 0; i < MAX; i++)
            {
                checkArray6(bytes);
            }
            sum += DateTime.Now - Time;
            Console.WriteLine($"{nameof(checkArray6)}:{sum.TotalMilliseconds / MAX}ms");

            Console.ReadLine();
        }

        /// <summary>
        /// 配列を0以外がくるまで回す。0以外が来ればfalse/そうでなければtrueを返す
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray(byte[] bytes)
        {
            foreach (var b in bytes)
                if (b != 0)
                    return false;
            return true;
        }
        /// <summary>
        /// 配列を0以外がくるまで回す。0以外が来ればfalse/そうでなければtrueを返す
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray2(byte[] bytes)
        {
            var imax = bytes.Length;
            for (var i = 0; i < imax; i++)
            {
                var b = bytes[i];
                if (b != 0)
                    return false;
            }
            return true;
        }
        /// <summary>
        /// 配列を0以外がくるまで回す。0以外が来ればfalse/そうでなければtrueを返す
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkEnumerable(IEnumerable<byte> bytes)
        {
            foreach (var b in bytes)
                if (b != 0)
                    return false;
            return true;
        }
        /// <summary>
        /// unchecked下で byteを0チェックする
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray3(byte[] bytes)
        {
            var imax = bytes.Length;
            unchecked
            {
                for (int i = 0; i < imax; i++)
                {
                    if (bytes[i] != 0)
                        return false;
                }

                return true;
            }
        }
        /// <summary>
        /// uncheckを使用した上でforで0に向かった方向で0チェックを行う
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray4(byte[] bytes)
        {
            unchecked
            {
                for (int i = bytes.Length - 1; i >= 0; i--)
                {
                    if (bytes[i] != 0)
                        return false;
                }

                return true;
            }
        }
        /// <summary>
        /// unsafeを使って ポインタを使った0チェックを行う
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray5(byte[] bytes)
        {
            var imax = bytes.Length;
            unchecked
            {
                unsafe
                {
                    fixed (byte* p = &bytes[0])
                    {
                        for (int i = 0; i < imax; i++)
                        {
                            if (*(p + i) != 0)
                                return false;
                        }
                    }

                    return true;
                }
            }
        }
        /// <summary>
        /// byte[]を long[]として0かどうかをチェックする
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        static bool checkArray6(byte[] bytes)
        {
            var imax = bytes.Length / 8;
            var あまり = bytes.Length % 8;
            unchecked
            {
                unsafe
                {
                    fixed (byte* p = &bytes[0])
                    {
                        var intp = (Int64*)p;
                        for (int i = 0; i < imax; i++)
                        {
                            if (*(intp + i) != 0)
                                return false;
                        }
                    }

                    if (あまり > 0)
                    {
                        fixed (byte* p = &bytes[imax * 8])
                        {
                            for (int i = 0; i < あまり; i++)
                            {
                                if (*(p + i) != 0)
                                    return false;
                            }
                        }
                    }

                    return true;
                }
            }
        }
    }
}
```