13

我正在尝试加快以下速度:

string s; //--> s is never null

if (s.Length != 0)
{
   <do something>
}

问题是,看起来 .Length 实际上计算了字符串中的字符,这比我需要的要多得多。有人知道如何加快速度吗?

或者,有没有办法确定 s[0] 是否存在,而无需检查字符串的其余部分?

4

9 回答 9

24

编辑:现在您已经提供了更多上下文:

  • 试图重现这一点,我根本找不到瓶颈string.Length。使它更快的唯一方法是注释掉测试和 if 块的主体- 这并不公平。只是注释掉条件会减慢速度,即无条件地复制引用比检查条件要慢。

  • 正如已经指出的那样,使用string.Split为您删除空条目的重载是真正的杀手级优化。

  • 您可以更进一步,避免每次都创建一个只有一个空格的新 char 数组。你总是会有效地传递同样的东西,那么为什么不利用它呢?

  • 空数组实际上是不可变的。您可以通过始终返回相同的内容来优化空/空情况。

优化后的代码变为:

private static readonly char[] Delimiters = " ".ToCharArray();
private static readonly string[] EmptyArray = new string[0];

public static string[] SplitOnMultiSpaces(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return EmptyArray;
    }

    return text.Split(Delimiters, StringSplitOptions.RemoveEmptyEntries);
}

String.Length绝对不计算字符串中的字母。该值存储为一个字段 - 尽管我似乎记得该字段的最高位用于记住是否所有字符都是 ASCII(或曾经是,无论如何)以启用其他优化。所以属性访问可能需要做一个位掩码,但它仍然是 O(1),我希望 JIT 也能内联它。(它被实现为extern,但希望在这种情况下不会影响 JIT - 我怀疑这是一个足够常见的操作,可能会获得特殊支持。)

如果您已经知道该字符串不为空,那么您现有的测试

if (s.Length != 0)

如果您正在寻找原始性能 IMO,这是最好的方法。在大多数情况下,我个人会写:

if (s != "")

为了更清楚地说明,我们对长度的兴趣并不在于它是否为空字符串。这将比长度测试稍慢,但我相信它更清楚。与以往一样,我会寻找最清晰的代码,直到您有基准/分析数据表明这确实一个瓶颈。我知道您的问题明确是关于找到最有效的测试,但我想我还是会提到这一点。你有证据证明这一个瓶颈吗?

编辑:只是为了更清楚地说明我使用的建议string.IsNullOrEmpty:对该方法的调用向我表明调用者正在明确尝试处理变量为空的情况,否则他们不会提到它。如果在代码的这一点上,如果变量空,则它被视为错误,那么您不应该尝试将其作为正常情况处理。

在这种情况下,Length检查实际上比我建议的不等式测试更好:它充当变量不为空的隐式断言如果您有错误并且它空,则测试将抛出异常并且会及早检测到错误。如果您使用相等性测试,它会将 null 视为与空字符串不同,因此它将进入您的“if”语句的主体。如果使用string.IsNullOrEmpty它会将 null 视为与空相同,因此它不会进入块中。

于 2010-08-02T17:27:14.683 回答
11

String.IsNullOrEmpty 是检查空字符串或零长度字符串的首选方法。

在内部,它将使用长度。但是,不应即时计算字符串的 Length 属性。

如果您绝对确定字符串永远不会为空,并且您对 String.IsNullOrEmpty 有强烈反对意见,那么我能想到的最有效的代码是:

if(s.Length > 0)
{
    // Do Something
}

或者,可能更好:

if(s != "")
{
    // Do Something
}
于 2010-08-02T17:25:10.367 回答
5

访问Length属性不应进行计数——.NET 字符串在对象内存储计数。

SSCLI/Rotor 源代码包含一个有趣的注释,表明String.Length(a) 高效且 (b) 神奇:

// Gets the length of this string
//
/// This is a EE implemented function so that the JIT can recognise is specially
/// and eliminate checks on character fetchs in a loop like:
/// for(int I = 0; I < str.Length; i++) str[i]
/// The actually code generated for this will be one instruction and will be inlined.
//
public extern int Length {
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    get;
}
于 2010-08-02T17:25:02.500 回答
3

这是函数String.IsNullOrEmpty -

if (!String.IsNullOrEmpty(yourstring))
{
  // your code
}
于 2010-08-02T17:25:13.450 回答
1
String.IsNullOrWhiteSpace(s);

如果 s 为 null 或 Empty,或者 s 仅由空白字符组成,则为 true。

于 2018-11-13T11:20:45.547 回答
0

根据您在回答中描述的意图,您为什么不尝试在拆分上使用此内置选项:

s.Split(new[]{" "}, StringSplitOptions.RemoveEmptyEntries);
于 2010-08-02T20:27:37.113 回答
0
        for (int i = 0; i < 100; i++)
        {
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            string s = "dsfasdfsdafasd";

            timer.Start();
            if (s.Length > 0)
            {
            }

            timer.Stop();
            System.Diagnostics.Debug.Write(String.Format("s.Length != 0 {0} ticks       ", timer.ElapsedTicks));

            timer.Reset();
            timer.Start();
            if (s == String.Empty)
            {
            }

            timer.Stop();
            System.Diagnostics.Debug.WriteLine(String.Format("s== String.Empty {0} ticks", timer.ElapsedTicks));
        }

使用秒表 s.length != 0 比 s == String.Empty 需要更少的滴答声

在我修复代码之后

于 2010-08-02T18:28:58.320 回答
0

与往常一样,性能:基准。
使用 C# 3.5 或更早版本,您需要测试yourString.LengthvsString.IsNullOrEmpty(yourString)

使用 C# 4,执行上述两项并添加String.IsNullOrWhiteSpace(yourString)

当然,如果您知道您的字符串永远不会为空,您可以尝试访问s[0]并处理不存在的异常。这通常不是好的做法,但它可能更接近您的需要(如果 s 应该始终具有非空白值)。

于 2010-08-02T17:30:59.760 回答
-1

只需使用String.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries)它,它就会为你做这一切。

于 2010-08-02T21:06:25.167 回答