对于已知案例,应避免尝试捕捉

时间:2017-11-23 09:13:27

标签: c# performance if-statement try-catch

我有一个案例,我知道会发生,但非常稀缺。例如,每运行一万次代码,这可能会发生一次。

我可以通过简单的if检查此案例,但此if会多次运行而无法使用。

另一方面,我可以将代码放在try-catch块中,当发生特殊情况时,我会执行恢复所需的操作。

问题是哪一个更好?我知道一般来说,try-catch不应该用于已知的情况,因为开销问题以及应用程序逻辑不应该依赖catch代码,但运行if多次会有更多的性能问题。我使用这个小测试代码对此进行了测试:

static void Main(string[] args)
{
    Stopwatch sc = new Stopwatch();
    var list = new List<int>();
    var rnd = new Random();
    for (int i = 0; i < 100000000; i++)
    {
        list.Add(rnd.Next());
    }

    sc.Start();
    DoWithIf(list);
    sc.Stop();
    Console.WriteLine($"Done with IFs in {sc.ElapsedMilliseconds} milliseconds");
    sc.Restart();
    DoWithTryCatch(list);
    sc.Stop();
    Console.WriteLine($"Done with TRY-CATCH in {sc.ElapsedMilliseconds} milliseconds");
    Console.ReadKey();
}

private static int[] DoWithTryCatch(List<int> list)
{
    var res = new int[list.Count ];
    try
    {
        for (int i = 0; i < list.Count; i++)
        {
            res[i] = list[i];
        }
        return res;
    }
    catch
    {
        return res;
    }
}

private static int[] DoWithIf(List<int> list)
{
    var res = new int[list.Count - 1];
    for (int i = 0; i < list.Count; i++)
    {
        if (i < res.Length)
            res[i] = list[i];
    }
    return res;
}

此代码只是将大量数字复制到数量不足的数组中。在我的机器中检查数组边界,每次运行 210毫秒,同时使用try-catch,一旦在 190毫秒附近运行,就会遇到catch。

另外,如果你认为这取决于我的情况,我会在应用程序中获得推送通知,并检查我是否有该消息的主题。如果不是,我将获取并存储下一条消息的主题信息。在很少的主题中有很多消息。

2 个答案:

答案 0 :(得分:3)

因此,可以准确地说,在您的测试中,if选项比try...catch选项慢20毫秒,循环次数为100000000次。
这相当于20 / 100,000,000 - 每次迭代 0.0000002毫秒

你真的认为那种纳米优化是值得编写违反正确设计标准的代码吗?

异常是针对特殊情况,即您无法控制或无法提前测试的事情 - 例如,当您从数据库中读取数据并且连接终止于中间时 - 这样的事情。 对于可以使用简单代码轻松测试的事物使用异常 - 嗯,这是完全错误的。

例如,如果您在这两个选项之间表现出有意义的性能差异,那么您可以证明使用try...catch代替if是合理的 - 但这显然不是这里的情况。

因此,总结一下 - 使用if,而不是try...catch

您应该为了清晰而设计代码,而不是为了提高性能。
编写代码,以最清晰的方式传达它正在实施的算法 设置性能目标并衡量代码的性能 如果您的代码不符合您的绩效目标,请找到瓶颈并对其进行处理 在设计代码时,不要浪费时间进行纳米优化。

答案 1 :(得分:2)

在你的情况下,你已经错过了明显的优化:如果你担心调用if 100.000次太多了......

private static int[] DoWithIf(List<int> list)
{
    var res = new int[list.Count - 1];

    var bounds = Math.Min(res.Length, list.Count)

    for (int i = 0; i < bounds; i++)
    {        
        res[i] = list[i];
    }
    return res;
}

所以我知道这只是一个测试案例,但答案是:如果你需要它,你可以根据需要进行优化。如果你在循环中有某些东西被认为是昂贵的,那么试着把它移出循环。 基于逻辑优化,而不是基于编译器结构。如果您要优化编译器结构,则不应该使用托管和/或高级语言进行编码。