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 配列を作成することを避けることで、さらに先に進むことができます。あなたは常に同じものを効果的に渡すつもりなので、それを利用してみませんか?

  • 空の配列は事実上不変です。常に同じものを返すことで、null/空のケースを最適化できます。

最適化されたコードは次のようになります。

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 であるかどうか (またはとにかく以前は ASCII であったかどうか) を記憶するために、そのフィールドの最上位ビットが使用されていることを覚えているようです。そのため、プロパティへのアクセスにはビットマスクが必要になる場合がありますが、それでも O(1) であり、JIT もインライン化することを期待しています。(これは として実装されてexternいますが、この場合は JIT に影響を与えないことを願っています。これは、特別なサポートを受ける可能性がある一般的な操作であると思われます。)

文字列がnullでないことがすでにわかっている場合は、既存のテスト

if (s.Length != 0)

生のパフォーマンス IMO を探している場合は、これが最適な方法です。個人的には、ほとんどの場合、次のように書きます。

if (s != "")

値としての長さにあまり関心がないことを明確にするために、これが空の文字列であるかどうかに関心があります。これは長さのテストよりもわずかに遅くなりますが、より明確になると思います。いつものように、これが実際にボトルネックであることを示すベンチマーク/プロファイリング データが得られるまで、私は最も明確なコードを求めます。あなたの質問が最も効率的なテストを見つけることについて明確に述べていることは知っていますが、とにかくこれについて言及したいと思いました. これがボトルネックであるという証拠はありますか?

編集:を使用しないという私の提案のより明確な理由を示すためstring.IsNullOrEmptyに: そのメソッドへの呼び出しは、呼び出し元が変数が null の場合を明示的に処理しようとしていることを示唆しています。コードのこの時点で、変数null の場合にバグとしてカウントされる場合は、通常のケースとしてそれを処理しようとするべきではありません。

この状況では、このLengthチェックは実際には、私が提案した不等式テストよりも優れている点があります。つまり、変数が null ではないという暗黙のアサーションとして機能します。バグがあり、それnull の場合、テストは例外をスローし、バグは早期に検出されます。等価テストを使用すると、null は空の文字列とは異なるものとして扱われるため、"if" ステートメントの本体に入ります。使うstring.IsNullOrEmptyとnullも空と同じ扱いになるのでブロックには入らない。

于 2010-08-02T17:27:14.683 に答える
11

String.IsNullOrEmpty は、null またはゼロの長さの文字列をチェックするための推奨される方法です。

内部的には、Length を使用します。ただし、文字列の Length プロパティはその場で計算するべきではありません。

文字列が決してnullにならないことが絶対に確実であり、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 に答える
0

いつものようにパフォーマンス: ベンチマーク。C# 3.5 以前を使用して、 vs
をテストする必要があります。yourString.LengthString.IsNullOrEmpty(yourString)

C# 4 を使用して、上記の両方を実行して追加しますString.IsNullOrWhiteSpace(yourString)

もちろん、文字列が空にならないことがわかっている場合はs[0]、例外が存在しないときにアクセスして処理を試みることができます。これは通常は良い習慣ではありませんが、必要なものに近いかもしれません (s が常に空白以外の値を持つ必要がある場合)。

于 2010-08-02T17:30:59.760 に答える
0

回答に記載されている意図に基づいて、Split でこの組み込みオプションを使用してみませんか。

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 に答える
-1

使用するだけString.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries)で、すべてが完了します。

于 2010-08-02T21:06:25.167 に答える