3

ネットワークから大量のデータを継続的に読み取るアプリケーションがあります。私たちは GC (gen0 コレクションの累積的な効果でさえも。調査結果をサポートするために ETW 収集を使用しました) を最大のボトルネックとして特定したため、コレクションが開始されないようにメモリ プールを実行しようとしました。

巨大なバイト配列を事前に割り当てて、割り当てなしでネットワークから継続的に読み取ることができます。char 配列でも同じことを実行できます (Encoding クラスでの変換中の割り当てを回避するため)。ただし、基本型 (int) に変換する方法はないようです。 、decimal ...) ホイールを再発明することなく (TryParse メソッドで BCL が行うことを再実装することを意味します)、またはガベージを作成せずに (char[] を破棄される文字列に変換します)。

だからここに私の質問があります:

  • どうにかして char 配列を文字列に挿入したり、文字列に再利用可能なメモリ プールからメモリを割り当てさせたりすることはできますか? 文字列の反映された内部を調べていましたが、不可能な作業のようですが、提案を歓迎します

また

  • いくつかの標準変換関数を利用して、char[] (または System.String 形式ではない他のテキスト) から基本型に変換することは可能ですか? 繰り返しますが、System.Number のリフレクション コードを調べていました。アンダーカバー関数は char * を使用しているように見えるため、リフレクション経由でそれらを呼び出すことができます。ただし、DateTime 変換では引き続き文字列が使用されます。

どんな提案も歓迎します。

4

1 に答える 1

1

安全でないコードがアプリケーションの実行可能な代替手段である場合は、文字列の内容と長さを書き換えることができます。これにより、再利用可能な文字列のプールを事前に割り当てることができるため、ガベージ コレクションを回避できます。

AC# 文字列は、次のようにメモリに配置されます。

int Capacity;
int Length;
char FirstCharacter;
// remaining characters follow

文字データはヌルで終了し (アンマネージ C/C++ コードとの相互運用を容易にするため)、現在の長さと最大容量も格納されるため、厄介なバッファー オーバーランの問題を回避できます。

新しいメモリを割り当てに既存の文字列に新しいコンテンツを挿入 する方法は次のとおりです。

    static unsafe void RecycleString(string s, char[] newcontents)
    {
        // First, fix the string so the GC doesn't move it around on us, and get a pointer to the character data.
        fixed (char* ps = s)
        {
            // We need an integer pointer as well, to check capacity and update length.
            int* psi = (int*)ps;
            int capacity = psi[-2];

            // Don't overrun the buffer!
            System.Diagnostics.Debug.Assert(capacity > newcontents.Length);
            if (capacity > newcontents.Length)
            {
                for (int i = 0; i < newcontents.Length; ++i)
                {
                    ps[i] = newcontents[i];
                }

                // Add null terminator and update length accordingly.
                ps[newcontents.Length] = '\0';
                psi[-1] = newcontents.Length;
            }
        }
    }

それが整っていれば、同じ文字列を心ゆくまでリサイクルして再解析できます。以下に簡単な例を示します。

    private static void ReusableStringTest()
    {
        char[] intFromWire = new char[] { '9', '0', '0', '0' };
        char[] floatFromWire = new char[] { '3', '.', '1', '4', '1', '5' };

        string reusableBuffer = new string('\0', 128);

        RecycleString(reusableBuffer, intFromWire);
        int i = Int32.Parse(reusableBuffer);
        Console.WriteLine("Parsed integer {0}", i);

        RecycleString(reusableBuffer, floatFromWire);
        float f = Single.Parse(reusableBuffer);
        Console.WriteLine("Parsed float {0}", f);
    }

生成された出力は、期待どおりです。

解析済み整数 9000
解析済みフロート 3.1415

そして、安全でないコードがあなたを不安にさせるなら、私たちが C と C++ でプログラミングに費やしてきたすべての年月を思い出してください

于 2015-03-23T06:21:34.723 に答える