【C#】Tupleで値を入れ替える時の処理速度

かなり前に Tuple で値を入れ替える方法を紹介しましたが、普通に入れ替えるのと、Tuple を使用して値を入れ替えるので処理速度がどの程度違うのか確認します。

takap-tech.com

確認環境

確認環境は以下の通りです。

  • .NET6 + C# 10.0
  • VisualStudio 2022
  • Windows11
  • BenchmarkDotNet
  • Ryzen 5900X
  • Relese ビルドをコンソールから実行して確認

確認用のコード

確認用のコードは以下の通りです。1000件の配列の値を逆順に入れ替えるために「通常の変数を使用した入れ替え」と「Tuple を使用した入れ替え」の2種類の速度を BenchmarkDotNet を使用して計測します。

using System;
using System.Linq;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace Takap.Performance
{
    public class AppMain
    {
        public static void Main(string[] args)
        {
            BenchmarkRunner.Run<ArrayReverseTest>();
        }
    }

    public class ArrayReverseTest
    {
        // (1) 一時変数を利用した通常の入れ替え
        [Benchmark]
        public void ArrayReverseSelf()
        {
            int[] testData = GetData();
            ArrayUtil.Reverse(testData);
        }

        // (2) Tuple を使用した入れ替え
        [Benchmark]
        public void ArrayReverseSelf_v2()
        {
            int[] testData = GetData();
            ArrayUtil.ReverseTuple(testData);
        }

        // テストデータの生成
        int[] _data;
        public int[] GetData()
        {
            return _data ??= Enumerable.Range(0, 1000).ToArray();
        }
    }

    public static class ArrayUtil
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Reverse<T>(T[] array)
        {
            if (array is null) throw new ArgumentException(nameof(array));

            int top = 0;
            int tail = array.Length - 1;
            T tmp = default;
            while (top < tail)
            {
                tmp = array[top];
                array[top] = array[tail];
                array[tail] = tmp;
                top++;
                tail--;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ReverseTuple<T>(T[] array)
        {
            if (array is null) throw new ArgumentException(nameof(array));

            int top = 0;
            int tail = array.Length - 1;
            T tmp = default;
            while (top < tail)
            {
                (array[top], array[tail]) = (array[tail], array[top]);
                top++;
                tail--;
            }
        }
    }
}

とくに以下の個所の処理速度の違いを確認します。

// (1) 一時変数を利用した通常の入れ替え
T tmp = default;
while (top < tail)
{
    tmp = array[top];
    array[top] = array[tail];
    array[tail] = tmp;

// (2) Tuple を使用した入れ替え
(array[top], array[tail]) = (array[tail], array[top]);

計測結果

計測結果は以下の通りです。

速度は同じです

|            Method |     Mean |   Error |  StdDev |
|------------------ |---------:|--------:|--------:|
|      ArrayReverse | 276.5 ns | 3.44 ns | 3.05 ns |
| ArrayReverseTuple | 276.0 ns | 2.43 ns | 2.28 ns |

件数を増やしても優位な差が出なかったので本当に同じなのだと思います。

参考までに、Tuple の値の入れ替えは以下のように展開され、変数がひとつ多く取られますがこの程度は差異にならないようです。

(array[top], array[tail]) = (array[tail], array[top]);

// ★こんな感じに展開される
// int num3 = num;
// int num4 = num2;
// T val = array[num2];
// T val2 = array[num];
// array[num3] = val;
// array[num4] = val2;