【问题标题】:Surprisingly bad c# switch performance令人惊讶的糟糕 c# 开关性能
【发布时间】:2017-04-10 06:41:51
【问题描述】:

我创建了一个TypeSwitch 类来使用类似于以下缩短示例的代码来转换我的字段:

static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>() 
{
    {typeof(Int16), 1},
    {typeof(Int32), 2},
    {typeof(Int64), 3},
    {typeof(IntPtr), 4},
    ...
    {typeof(String), 18}
};

public static object ConvertFromDBValue(Type type, object value) 
{
    try 
    {
        switch (TypeDefs[type]) 
        {
            case 1: // {typeof(Int16), 1},
            {
                return Convert.ToInt16(value);
            }
            case 2: // {typeof(Int32), 2},
            {
                return Convert.ToInt32(value);
            }
            case 3: // {typeof(Int64), 3},
            {
                return Convert.ToInt64(value);
            }
            ...
            ...
            ...
            case 17: // {typeof(Char), 17},
            case 18: // {typeof(String), 18},
            {
                return value.ToString().Trim();
            }
            default: 
            {
                return value;
            }
        }
    }
    catch (Exception ex) 
    {
        throw ex;
    }
}

使用检测工具,我看到超过 60% 的时间花在上述ConvertFromDBValue 的函数体中,即由于 switch(或 try-catch)而花费的时间比在 @ 中查找 Type 值要多987654324@ 并转换值(例如Convert.ToInt32)。实际上,我在函数体中花费的时间是 Dictionary.get_Item 的 3 倍...

这让我有些吃惊 - 谁能确认switch 慢得多,或者还有其他原因吗?!

更新 我删除了 try-catch 部分,但这并没有太多...

【问题讨论】:

  • 我怀疑是switch 让你慢了下来。我怀疑这是需要时间的价值的拳击。如果您的字典是强类型的,那么它的工作速度会非常快。
  • switch 应该很快。这取决于。你调用这个方法多少次?不要一遍又一遍地使用 try-catch,因为那太慢了。
  • 顺便说一句:(与您的性能问题无关)像Dictionary&lt;Type, Func&lt;object,object&gt;&gt; 这样的字典可以是一个更简洁的解决方案,它消除了使用 switch .... @ 987654330@
  • @L.B 感谢您的提示。
  • 我想是的,因为我建议了。

标签: c# performance switch-statement


【解决方案1】:

正如其他人所说,您正在支付装箱/拆箱罚款,如果可能,您应该尝试消除它。

至于方法的主体,您应该摆脱字典并使用 if if elses 链 - 它应该会给您带来可衡量的性能改进。


编辑

在 Hogan 发表评论后,我对其进行了测试,令我惊讶的是 dict+catchif 之间的差异小于 5%-15% ( if 稍快)在下面我不完美的测试中。

Dict+case: 987.0945ms
Ifs: 937.5104ms
Hogan's array of funcs: 854.4887ms

测试:

class Program
{
    static Dictionary<Type, int> TypeDefs = new Dictionary<Type, int>()
    {
        {typeof(Int16), 1},
        {typeof(Int32), 2},
        {typeof(Int64), 3},
        {typeof(IntPtr), 4},
        {typeof(char), 5},
        {typeof(String), 6}
    };

    static KeyValuePair<Type,object>[] _Types = new[] 
        { new KeyValuePair<Type,object> ( typeof(Int16),5 ),
        new KeyValuePair<Type,object> (typeof(Int32),57 ),
        new KeyValuePair<Type,object> (typeof(Int64),157 ),
        new KeyValuePair<Type,object> (typeof(IntPtr),new IntPtr(6) ),
        new KeyValuePair<Type,object> (typeof(String),"Hello!" ),
    };

    public static object ConvertFromDBValue(Type type, object value)
    {
        try
        {
            switch (TypeDefs[type])
            {
                case 1: // {typeof(Int16), 1},
                    {
                        return Convert.ToInt16(value);
                    }
                case 2: // {typeof(Int32), 2},
                    {
                        return Convert.ToInt32(value);
                    }
                case 3: // {typeof(Int64), 3},
                    {
                        return Convert.ToInt64(value);
                    }
                case 4: // {typeof(IntPtr), 4},
                    {
                        return value;
                    }
                case 5: // {typeof(Char), 17},
                case 6: // {typeof(String), 18},
                    {
                        return value;
                    }
                default:
                    {
                        return value;
                    }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public static object ConvertFromDBValue2(Type type, object value)
    {
        try
        {
            if (type == typeof(Int16))
            {
                return Convert.ToInt16(value);
            }
            if (type == typeof(Int32))
            {
                return Convert.ToInt32(value);
            }
            if (type == typeof(Int64))
            {
                return Convert.ToInt64(value);
            }
            if (type == typeof(IntPtr))
            {
                return (IntPtr)value;
            }
            if (type == typeof(Char) || type == typeof(String))
            {
                return value.ToString().Trim();
            }
            return value;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }


    static Func<object, object>[] funcList =
    {
        (value) => value,                    //0
        (value) => Convert.ToInt16(value),   //1
        (value) => Convert.ToInt32(value),   //2
        (value) => Convert.ToInt64(value),   //3
        (value) => value,   //4
        (value) => value,   //5
        (value) => value,   //6
        (value) => value,   //7
        (value) => value,   //8
        (value) => value,   //9
        (value) => value,   //10
        (value) => value,   //11
        (value) => value,   //12
        (value) => value,   //13
        (value) => value,   //14
        (value) => value,   //15
        (value) => value,   //16
        (value) => value,   //17
        (value) => value.ToString().Trim() //18
    };

    public static object ConvertFromDBValueHogan(Type type, object value)
    {
    return funcList[TypeDefs[type]](value);
    }

    static void Main(string[] args)
    {
        var sw = new System.Diagnostics.Stopwatch();
        Random random = new Random(113453113);


        sw.Start();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p = ConvertFromDBValue(testValue.Key, testValue.Value);
        }
        var elapsed = sw.Elapsed;
        Console.WriteLine($"Dict+case: {elapsed.TotalMilliseconds}ms");

        sw.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p2 = ConvertFromDBValue2(testValue.Key, testValue.Value);
        }
        elapsed = sw.Elapsed;
        Console.WriteLine($"Ifs: {elapsed.TotalMilliseconds}ms");

        sw.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            var x = random.Next(5);
            var testValue = _Types[x];
            var p3 = ConvertFromDBValueHogan(testValue.Key, testValue.Value);
        }
        elapsed = sw.Elapsed;
        Console.WriteLine($"Hogan's array of funcs: {elapsed.TotalMilliseconds}ms");
        Console.ReadLine();
    }
}

【讨论】:

  • 好吧,我会试试的 - 我明白了 - if-else 对于列表末尾的项目来说速度较慢,但​​作为字典访问速度仍然更快......至于拳击,我打开了一个关于它的新问题here...
  • 这是错误的,字典应该比链式 if-else 和 switch 更快——除非你对频率有所了解。
【解决方案2】:

如果我的“函数数组和要选择的数组索引”不清楚,这里是一些示例代码。

Func<object, object>[] funcList =
   { (value) => value,                    //0
     (value) => Convert.ToInt16(value),   //1
     (value) => Convert.ToInt32(value),   //2
     (value) => Convert.ToInt64(value),   //3
     (value) => value,   //4
     (value) => value,   //5
     (value) => value,   //6
     (value) => value,   //7
     (value) => value,   //8
     (value) => value,   //9
     (value) => value,   //10
     (value) => value,   //11
     (value) => value,   //12
     (value) => value,   //13
     (value) => value,   //14
     (value) => value,   //15
     (value) => value,   //16
     (value) => value,   //17
     (value) => value.ToString().Trim() //18
   }; 

public static object ConvertFromDBValue(Type type, object value)
{
  if (TypeDefs[type] <= 18)
    return funcList[TypeDefs[type]](value);
  else
   return value;
}

如果您可以保证没有大于 18 的值,请取出 if (TypeDefs[type] &lt;= 18) if 语句,以便更快。

这是未经测试的示例代码。

【讨论】:

  • TypeDefs + funcList 只是为了获得代表?为什么不像我的评论中所说的那样简单地使用字典? var val = TypeDefs[typeof(short)]("12345")
  • 实际上是一票否决,因为此方法中有两个字典访问调用......
  • @neggenbe - 任何优化器都只会在这里调用一次。
  • @L.B -- 好点子 -- 我没有看 TyepDefs 变量是如何定义的,我只是展示了一种将 swtich 转换为数组引用的好方法。
猜你喜欢
  • 1970-01-01
  • 2022-06-11
  • 1970-01-01
  • 2020-09-17
  • 2015-05-13
  • 1970-01-01
  • 2019-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多