3

string.Formatそのため、大学の教授は、C# で文字列を連結すると (つまり、プラス記号演算子を使用すると) メモリの断片化が発生するため、代わりに使用する必要があると教えてくれました。

今、私はスタック オーバーフローで多くの検索を行い、パフォーマンスに関する多くのスレッドを見つけました。これは、文字列の連結が圧倒的に有利です。(一部には、これこれ、およびこれが含まれます)

ただし、メモリの断片化について話している人は見つかりません。string.FormatILspy を使用して.NET を開いたところ、メソッドと同じ文字列ビルダーを使用しているようです (符号がオーバーロードされてstring.Concatいることを理解していれば)。+実際: string.Concat!のコードを使用します。

この記事は 2007 年に見つけましたが、今日 (またはこれまでも) 正確であるとは思えません。どうやらコンパイラは今日それを避けるのに十分賢いので、問題を再現できないようです。string.format とプラス記号を使用して文字列を追加すると、内部的に同じコードが使用されます。前に述べたように、string.Format は string.Concat が使用するのと同じコードを使用します。

だから今、私は彼の主張を疑い始めている. 本当ですか?

4

2 に答える 2

22

そのため、大学の教授から、C# で文字列に連結を使用すると (つまり、プラス記号演算子を使用すると) メモリの断片化が発生するため、代わりに string.Format を使用する必要があると言われました。

いいえ、代わりにすべきことは、ユーザー調査を行い、ユーザーに焦点を当てた実際のパフォーマンス指標を設定し、それらの指標に対してプログラムのパフォーマンスを測定することです. パフォーマンスの問題が見つかった場合にのみ、適切なプロファイリング ツールを使用して、パフォーマンスの問題の原因を特定する必要があります。原因が「メモリの断片化」である場合は、「断片化」の原因を特定し、実験を試みて影響を軽減する手法を特定することで、それに対処します。

「文字列の連結を避ける」などの「ヒントとコツ」ではパフォーマンスは達成されません。パフォーマンスは、工学分野を現実的な問題に適用することによって達成されます。

より具体的な問題に対処するには:パフォーマンス上の理由から、フォーマットを優先して連結を避けるというアドバイスを聞いたことがありません。通常与えられるアドバイスは、builders優先して反復連結を避けることです。連結の反復は、時間と空間の二次関数であり、コレクション プレッシャーを生み出します。ビルダーは不要なメモリを割り当てますが、一般的なシナリオでは線形です。どちらもマネージ ヒープの断片化を作成しません。連結を繰り返すと、連続するガベージ ブロックが生成される傾向があります。

マネージ ヒープの不必要な断片化に至るパフォーマンスの問題が発生した回数は、まさに 1 回です。Roslyn の初期のバージョンでは、小さな長命オブジェクト、次に小さな短命オブジェクト、小さな長命オブジェクトの順に割り当てるパターンがありました... 数十万回続けて、結果として最大に断片化されたヒープコレクションでユーザーに影響を与えるパフォーマンスの問題を引き起こしました。これは、快適な椅子からコードをアドホックに分析するのではなく、関連するシナリオでのパフォーマンスを 注意深く測定することによって決定しました。

通常のアドバイスは、断片化を避けることではなく、プレッシャーを避けることです。Roslyn の設計中に、前述の割り当てパターンの問題が修正されると、フラグメンテーションよりもプレッシャーの方が GC パフォーマンスにはるかに大きな影響を与えることがわかりました。

あなたへの私のアドバイスは、教授に説明を求めるか、パフォーマンスメトリクスに対してより規律あるアプローチをしている教授を見つけることです.

とはいえ、連結の代わりにフォーマットを使用する必要がありますが、パフォーマンス上の理由からではありません。むしろ、コードの読みやすさ、ローカライズのしやすさ、および同様のスタイル上の懸念のためです。フォーマット文字列をリソースにしたり、ローカライズしたりできます。

最後に、ユーザーに提供する SQL クエリや HTML ブロックのようなものを作成するために文字列を組み合わせる場合は、これらの手法を使用しないでください。文字列構築のこれらのアプリケーションは、間違った使い方をすると、セキュリティに深刻な影響を与えます。文字列を独自に作成するのではなく、これらのオブジェクトの構築用に特別に設計されたライブラリとツールを使用してください。

于 2016-05-10T19:43:00.880 に答える
0

文字列連結の問題は、文字列が不変であることです。string1 + string2 は string2 を string1 に連結せず、まったく新しい文字列を作成します。StringBuilder (または string.Format) を使用すると、この問題は発生しません。内部的に、StringBuilder は char[] を保持しますが、これは過剰に割り当てられます。StringBuilder に何かを追加しても、char[] のスペースが不足しない限り、新しいオブジェクトは作成されません (この場合、新しいオブジェクトが過剰に割り当てられます)。

簡単なベンチマークを実行しました。私はそれが要点を証明していると思います:)

        StringBuilder sb = new StringBuilder();
        string st;
        Stopwatch sw;

        sw = Stopwatch.StartNew();

        for (int i = 0 ; i < 100000 ; i++)
        {
            sb.Append("a");
        }

        st = sb.ToString();

        sw.Stop();
        Debug.WriteLine($"Elapsed: {sw.Elapsed}");

        st = "";

        sw = Stopwatch.StartNew();

        for (int i = 0 ; i < 100000 ; i++)
        {
            st = st + "a";
        }

        sw.Stop();
        Debug.WriteLine($"Elapsed: {sw.Elapsed}");

コンソール出力:

経過: 00:00:00.0011883 (StringBuilder.Append())

経過時間: 00:00:01.7791839 (+ 演算子)

于 2016-05-10T19:15:09.357 に答える