代码之家  ›  专栏  ›  技术社区  ›  James Jonatah

切换语句over set语句

  •  0
  • James Jonatah  · 技术社区  · 2 年前

    我正在进行一个关于ref structs和text ValueType的测试
    并且在结构构造函数中工作时遇到了这个疑问。

    显然,用i样式循环控制ref变量的switch语句可以
    比在结构的字段中手动设置数组位置更快。
    我不确定这种测试在顶层陈述方法中的有效性,但是

    这是代码

    using System.Diagnostics;
    
    const string testText = $"""
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.  
        Quisque orci purus, vulputate vulputate
        """;
    const   int     load_Steps  =  250;
    CharblockExample bySetBlock ;
    CharblockExample bySwitchBlock;
    var watcher = Stopwatch.StartNew();
    var results = new List<(TimeSpan tic1, TimeSpan tic2)>();
    try
    {
        for (int i = 0; i < load_Steps; i++)
        {
            var start_tics = watcher.Elapsed;
            bySwitchBlock = new(testText.AsSpan(), true);
            var ticBlock2 = watcher.Elapsed - start_tics;
    
            start_tics = watcher.Elapsed;
            bySetBlock = new(testText.AsSpan(), false);
            var ticBlock1 = watcher.Elapsed - start_tics;
            
            Console.WriteLine($"{nameof(bySetBlock)} and {nameof(bySwitchBlock)}: {ticBlock1} vs {ticBlock2}");
            results.Add((ticBlock1, ticBlock2));
        }
    }
    finally
    {
        watcher.Stop();
        Console.WriteLine();
        var overall_1 = results.Sum(element => element.tic1.Microseconds);
        var overall_2 = results.Sum(selector => selector.tic2.Microseconds);
        var overall_result = overall_1 - overall_2;
    
        Console.WriteLine($"overall analysis: {(overall_1 is < 0
            ? $"{nameof(bySetBlock)}    was {Math.Abs(overall_result)} ms faster than {nameof(bySwitchBlock)}" 
            : $"{nameof(bySwitchBlock)} was {Math.Abs(overall_result)} ms faster than {nameof(bySetBlock)}"
        )}");
    }
    
    public readonly ref struct CharblockExample
    {
        private readonly int inputtedLength;
        private readonly bool _input = false;
        private readonly char //char spots
        #region Char-Spots
            _0, _1, _2, _3, _4, _5,
            _6, _7, _8, _9, _10;
        #endregion
    
        public CharblockExample(ReadOnlySpan<char> chars, bool performTest)
        {
            _input = true;
            Span<char> arr = stackalloc char[MAX_LENGTH];
            inputtedLength = chars.Length;
            if (chars.Length > MAX_LENGTH)
                throw new ArgumentOutOfRangeException(nameof(chars), $"""
                    The provided char array is longer than the max of 256 chars for this block.
                    """);
    
            for (int i = 0; i < MAX_LENGTH; i++)
            {
                if (i >= chars.Length) break;
                arr[i] = chars[i];
            }
    
            if (performTest)
            {
                int pos = 0;
                ref var _ref = ref _1;
                while (pos < MAX_LENGTH - 1)
                {
                    switch (pos)
                    {
                        case 0: _ref = ref _0; break;
                        case 1: _ref = ref _1; break;
                        case 2: _ref = ref _2; break;
                        case 3: _ref = ref _3; break;
                        case 4: _ref = ref _4; break;
                        case 5: _ref = ref _5; break;
                        case 6: _ref = ref _6; break;
                        case 7: _ref = ref _7; break;
                        case 8: _ref = ref _8; break;
                        case 9: _ref = ref _9; break;
                        case 10: _ref = ref _10; break;
                    }
                    pos++;
                    _ref = arr[pos];
                }
            }
            else
            {
                _0 = arr[0]; _1 = arr[1]; _2 = arr[2]; _3 = arr[3]; _4 = arr[4];
                _5 = arr[5]; _6 = arr[6]; _7 = arr[7]; _8 = arr[8]; _9 = arr[9];
                _10 = arr[10];
            }
        }    
        public char[] CharArr => new[]
        {
            _0,   _1,   _2,   _3,   _4,   _5,
            _6,   _7,   _8,   _9,  _10
        };
    
        private const int MAX_DISPLAYABLE = 15;
        private const int MAX_LENGTH = 256;
    }
    

    这是我完全执行后的结果:

    总体分析:bySwitchBlock比bySetBlock快1240ms

    • 根据我的判断,只有在不检查任何布尔逻辑或推进整数索引器的情况下进行设置才能更快,因为它只是针对我所看到的命令的在线执行
    1 回复  |  直到 2 年前
        1
  •  3
  •   Sweeper    2 年前

    测试 CharblockExample 来自 first revision 使用 BenchmarkDotNet 给出了使用 switch 比不使用它慢大约2倍。

    BenchmarkDotNet v0.13.7,Windows 10(10.0.19045.3086/22H2/2022更新)

    Intel Core i7-6700 CPU 3.40GHz(Skylake),1个CPU,8个逻辑核和4个物理核

    .NET SDK 7.0.304

    [主机]:.NET 7.0.7(7.0.723.27404),X64 RyuJIT AVX2

    默认作业:.NET 7.0.7(7.0.723.27404),X64 RyuJIT AVX2

    方法 意思是 错误 标准偏差
    转换 363.3纳秒 7.18纳秒 11.18 ns
    无开关 170.2纳秒 3.44纳秒 3.96纳秒

    这是意料之中的事。你在那里做了很多额外的工作——控制循环,用跳表跳来跳去等等。如果抖动能够识别模式,并优化无开关代码中的一系列分配,使其更快,我也不会感到惊讶。

    以下是基准代码:

    public class Benchmark
    {
        const string testText = $"""
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.  
        Quisque orci purus, vulputate vulputate mattis quis, 
        accumsan in turpis. Vestibulum quis sapien iaculis, 
        pulvinar magna sed, lacinia lectus. Donec nec felis nec
         metus dictum aliquet vel non.
        """;
    
        [Benchmark]
        public CharblockExample Switch() => new CharblockExample(testText, true);
    
        [Benchmark]
        public CharblockExample NoSwitch() => new CharblockExample(testText, false);
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Benchmark>();
        }
    }
    

    一般来说,像这样的微基准测试代码会产生相当不准确的结果。许多外部因素会影响最终结果。您应该使用