【问题标题】:Does string.IsNullOrEmpty have performance issues?string.IsNullOrEmpty 有性能问题吗?
【发布时间】:2013-08-26 14:46:58
【问题描述】:

这两种检查字符串的方式有什么区别吗?

if(!string.IsNullOrEmpty(myString))
{
  //Do something
}

if(myString != null && myString != "")
{
  //Do something
}

到目前为止,我虽然没有,但有人这么说。他说第二个更好,因为第一个需要方法调用。

我有点困惑。

【问题讨论】:

  • 运行一些基准测试。至少做 100,000 次,多次并测量性能。看看哪个更快)。
  • 此人是否听说过内联,并给出了它在这里不适用的原因?不?那么它甚至可能不值得进行基准测试,他们可能不知道他们在说什么。
  • @ChrisF 我喜欢这个问题。我认为这些问题出现在 SO 上是件好事。人们阅读它并可以在必要时更改代码。我猜你不必告诉他他只需要做基准测试,其他人也不想对此做出回应。我希望您的评论听起来不像“自己动手”...
  • @Joetjah - 我同意你的观点,但如果它表明 OP 进行了一些测试并发现了他们不理解的东西,这可能是一个好问题。

标签: c#


【解决方案1】:

不,它没有性能问题。以下是它的实现方式:

public static bool IsNullOrEmpty(string value)
{
    if (value != null)
    {
        return (value.Length == 0);
    }
    return true;
}

我会一直使用IsNullOrEmpty,而不是手动编写这两个条件。它的可读性更高,并且使用它不会产生任何性能成本。

【讨论】:

  • 加上它很可能被内联的事实。提及它可能很重要,因为这意味着没有由于方法调用而产生的开销。
  • 其实是有区别的;请参阅我的答案中的基准。
  • 是的,这就是 IsNullOrEmpty 比 == "" 快的原因
【解决方案2】:

和其他回答的人一样,我认为不会有性能差异,但为了确定,我运行了一个基准测试。结果并不完全符合我的预期......

这是我的基准测试的代码:

using System;
using System.Diagnostics;

static class Program
{
    static void Main()
    {
        int count = 1;

        // First run for JIT warm-up
        IsNullOrEmpty(null, count);
        TestEqualsEmpty(null, count);
        TestLengthZero(null, count);

        count = 1000000000;

        Console.WriteLine("Case 1: s == \"test\"");
        RunTests("test", count);

        Console.WriteLine("Case 2: s == null");
        RunTests(null, count);

        Console.WriteLine("Case 3: s == \"\"");
        RunTests("", count);
    }

    static void RunTests(string s, int count)
    {
        var ts = IsNullOrEmpty(s, count);
        Console.WriteLine("\tIsNullOrEmpty:         {0}", ts);

        ts = TestLengthZero(s, count);
        Console.WriteLine("\tTest if s.Length == 0: {0}", ts);

        ts = TestEqualsEmpty(s, count);
        Console.WriteLine("\tTest if s == \"\":       {0}", ts);
    }

    static TimeSpan IsNullOrEmpty(string s, int count)
    {
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            if (string.IsNullOrEmpty(s))
            {
            }
        }
        sw.Stop();
        return sw.Elapsed;
    }

    static TimeSpan TestLengthZero(string s, int count)
    {
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            if (s == null || s.Length == 0)
            {
            }
        }
        sw.Stop();
        return sw.Elapsed;
    }

    static TimeSpan TestEqualsEmpty(string s, int count)
    {
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            if (s == null || s == "")
            {
            }
        }
        sw.Stop();
        return sw.Elapsed;
    }
}

结果如下:

案例1:s ==“测试” IsNullOrEmpty:00:00:00.6000748 测试 s.Length == 0: 00:00:00.5566793 测试 s == "": 00:00:02.2284007 案例2:s == null IsNullOrEmpty: 00:00:00.5556170 测试 s.Length == 0: 00:00:00.5569102 测试 s == "": 00:00:00.5554338 案例 3:s == "" IsNullOrEmpty:00:00:00.5568344 测试 s.Length == 0: 00:00:00.5556285 测试 s == "": 00:00:03.0626445

(在启用优化的情况下编译;这些是 32 位 CLR 的结果,但 64 位 CLR 的结果相似)

如您所见,如果字符串不为空,调用IsNullOrEmpty 比比较null"" 快​​得多,几乎和比较null 并测试长度是否一样快0. 如果字符串为空,则三种方法的性能相同。

所以,这样做:

if(myString != null && myString != "")

可能会比这更慢,而不是更快:

if(string.IsNullOrEmpty(myString))

你可以这样做:

if(myString != null && myString.Length > 0)

但与调用IsNullOrEmpty相比,性能提升会非常小,而且会损害可读性。

请注意,此基准测试运行了 1,000,000,000(10 亿)次迭代;这对于注意到实际差异是必要的。在现实世界的场景中,差异可能很小而无法注意到。这显然是一个微优化。

【讨论】:

  • 其实,这正是我所期望的。想一想,如果您与 null 或 length 进行比较,这些是一些比较(因为长度也被存储)。字符串比较是一个更通用的东西。根据编译器设置和运行时版本,我希望编译器可以针对硬编码字符串执行优化,而不是一定要这样做。
  • 您是否验证了编译器不会删除测试?毕竟你什么都没做,所以如果比较没有任何副作用,整个块可以安全地被优化掉(我不知道它是否确实如此)。
  • @BrianRasmussen,好点;我刚刚检查了 ildasm,IL 中没有删除测试。我不知道 JIT 是否会进行这种优化,不过...
  • 如果你检查.Net框架的String.cs源文件,你会看到IsNullOrEmpty不检查== "",因为它很慢,它检查.Length == 0!这就是为什么您的基准测试结果出乎您的意料。 :) 对我来说正确答案是 Darin Dimitrov。
  • 第一个样本的 00:00:00.5566793 和 00:00:00.6000748 不能被视为非常重要并证明存在差异。测试应该以不同的顺序运行多次,我们应该取测试之间的平均值。
【解决方案3】:

我会说,即使 IsNullOrEmpty 有点慢,也可以使用它,因为与使用奇怪的 IF 条件相比,它更容易混淆且更具可读性。

看下面的例子

if((!string.IsNullOrEmpty(str1)) && (!string.IsNullOrEmpty(str2)) && (!string.IsNullOrEmpty(str3)))
{
//Code here
}

比后面更易读

if(str1 != null && str1 != "" && str2 != null && str2 != "" && str3 != null && str3 != "")
{
// Code here
}

【讨论】:

    【解决方案4】:

    您当前的代码很好,并且您不应该有任何性能差异。

    目前实现为:

     public static bool IsNullOrEmpty(String value) 
     {
         return (value == null || value.Length == 0);
     }
    

    Source code take from HERE

    如果性能上会有差异,那么它必须可以忽略不计。对于此类情况,您应该始终考虑更具可读性的代码。

    您可能还会看到:Premature-Optimization and Performance Anxiety

    【讨论】:

      【解决方案5】:

      如果您查找执行时的响应时间,您可以查看http://www.dotnetperls.com/isnullorempty 上的“替代方案”部分。

      我也认为,IsNullOrEmpty 方法的方式比其他方式更干净。

      【讨论】:

        【解决方案6】:

        http://blogs.msdn.com/b/ericgu/archive/2004/01/29/64717.aspx -- 这描述了有 CLR 函数被自动内联。仅仅因为您今天对其进行分析并看到差异并不意味着下次运行它时它会保持不变。如果您真的关心性能,您可以避免潜在的函数调用,因为您手动显式内联它。确实,请保存此类微优化,直到您对其进行分析并看到您有需要。

        .Net 的未来版本还可以将 IsNullOrEmpty() 的实现更改为优化的本机代码调用。

        【讨论】:

          【解决方案7】:

          当你调用一个函数时,额外的汇编指令用于在调用栈上放置另一个栈帧。一种优化方法是删除所有这些冗余调用并用内联代码替换它们(正如“某人”所建议的那样)。但是,几乎不需要在此级别进行优化。

          这种速度上的差异基本上是单次调用无法测量的。像您的代码一样,可读代码应始终被重视而不是不必要的优化。

          【讨论】:

          • 内联优化由编译器和 JIT 执行。他们几乎总是比你做得更好。
          猜你喜欢
          • 2014-09-12
          • 2019-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多