4

FileStream.Write 関数の使用中にパフォーマンスの問題に直面しています。

StreamReader オブジェクトを使用してファイル (~ サイズは 400 KB) から Base64 文字列を読み取るために使用するコンソール アプリケーションがあります。Convert.FromBase64String を使用して、この文字列をバイト配列に変換します。次に、FileStream オブジェクトを使用して、このバイト配列をファイルに書き込みます。ここで得られたバイト配列の長さは 334991 でした。

バイト配列の書き込みにかかる時間を測定したところ、約0.116 秒でした。

楽しみのために、ASCIIEncoding.GetBytes 関数を使用して、同じ Base64 でエンコードされた文字列からバイト配列を取得しました (これでは正しい DECODED 出力が得られないことはわかっていましたが、試してみたかっただけです)。FileStream オブジェクトを使用して、このバイト配列をファイルに書き込みました。ここで得られたバイト配列の長さは 458414 でした。

この方法論を使用してバイト配列の書き込みにかかる時間を測定したところ、約0.008 秒であることがわかりました。

サンプルコードは次のとおりです。

class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopWatch = new Stopwatch();
        TimeSpan executionTime;

        StreamReader sr = new StreamReader("foo.txt");
        string sampleString = sr.ReadToEnd();
        sr.Close();

        ////1. Convert to bytes using Base64 Decoder (The real output!)
        //byte[] binaryData = Convert.FromBase64String(sampleString);

        //2. Convert to bytes using AsciiEncoding (Just for Fun!)
        byte[] binaryData = new System.Text.ASCIIEncoding().GetBytes(sampleString);
        Console.WriteLine("Byte Length: " + binaryData.Length);

        stopWatch.Start();
        FileStream fs = new FileStream("bar.txt", FileMode.Create, FileAccess.Write);
        fs.Write(binaryData, 0, binaryData.Length);
        fs.Flush();
        fs.Close();
        stopWatch.Stop();

        executionTime = stopWatch.Elapsed;
        Console.WriteLine("FileStream Write - Total Execution Time: " + executionTime.TotalSeconds.ToString());
        Console.Read();
    }
}

Base64 でエンコードされた文字列を含む約 5000 個のファイルに対してテストを実行しましたが、これら 2 種類のバイト配列の書き込みにかかる時間の差はほぼ 10 倍です ( 実際のデコードを使用してバイト配列を書き込む方が時間がかかります)。

Convert.FromBase64String を使用して取得したバイト配列の長さは、ASCIIEncoding.GetBytes 関数を使用して取得したものよりも短くなっています。

私がやろうとしているのは、FileStream オブジェクトを使用して大量のバイトを書き込むことだけなのだろうか。では、バイト配列をディスクに書き込む際に、なぜこのような大幅なパフォーマンスの違い (書き込み時間) が発生するのでしょうか?

それとも、私は何かひどく間違ったことをしていますか? お知らせ下さい。

4

4 に答える 4

1

まず、DateTime の解像度が低い (iirc 0.018 秒)。したがって、ストップウォッチ クラスを使用することをお勧めします。

これは違いを完全には説明していませんが、より良い数値が得られるかもしれません。

于 2009-05-15T07:59:54.307 に答える
1

私は別の質問にいくつかの同様のアドバイスをしました .MS Researchからのこれらのツールと参考文献をチェックしてください.

これらは、潜在的な I/O 問題を解決する、または少なくともそれらを理解するのに役立ちます。

また、CLR LARGE オブジェクト ヒープに関する問題にも注意する必要があります。特に配列を使用する場合 (同じプロセスでこれを 5000 回実行した場合、80kb を超えるものはマネージド ヒープの相互作用が最適ではありません)。

しかし、本当に、もう一度見てみると、これらはあなたの補題とそれほど密接に関連しているとは思いません。このコードをプロファイラーで実行したところ、Convert. Base64はすべてのサイクルを消費しています。

テストコードでは、常に2回以上連続してテストを実行する必要があります。ジッターはランタイム負荷に影響を与える可能性があります。これにより、実行時間に驚くほどの変動が生じる可能性があります。現時点では、ジッターと大きなオブジェクト ヒープの影響を考慮して、テスト ハーネスを再評価する必要があると思います。(これらのルーチンの 1 つを他のルーチンの前に置きます...)。

于 2009-05-15T08:00:48.470 に答える
1

コードの主な問題は、キャベツとニンジンを比較しようとしていることだと思います (フランス語の表現):

Convert.FromBase64String と ASCIIEncoding().GetBytes はまったく同じことをしません。

プログラムへの入力として任意のテキスト ファイルを使用しようとすると、ASCIIEncoding で正常に実行されているときに FromBase64 で失敗します。

次に、パフォーマンス ヒットについて説明します。

  • ASCIIEncoding().GetBytes は、ファイルから 1 文字を取得し、それをバイトに変換するだけです (これはかなり簡単です。何もする必要はありません)。たとえば、「A」は 0x41 に、「Z」は 0x5A に変換されます...

  • Convert.FromBase64String の場合は、別の話です。実際には、「base64 でエンコードされた文字列」をバイト配列に変換しています。base64 文字列は、たとえば、「バイナリ データの印刷可能な表現」です。より良いのは、バイナリ データの「テキスト」表現であり、たとえば、インターネット回線経由で送信できるようにすることです。メールの画像は base64 でエンコードされているため、メール プロトコルはテキスト ベースであるため、base64 をバイトに変換するプロセスは単純ではなく、パフォーマンスが低下します。

参考までに、base64 文字列は次のようになります。

SABLAGwaAbABvAHcAIABXAG8AcgBsAGQAIQA=

これは「Hello World!」に変換されます。すぐじゃないですよね?

base64 形式に関する情報は次のとおりです: http://en.wikipedia.org/wiki/Base64

お役に立てれば

于 2009-05-15T08:19:28.270 に答える
0

この問題について Jon Skeet が最近書いた一連の記事 (および付随するソース プロジェクト) を参照してください。

ここここ

具体的には、彼はバッファリングとストリーミングを比較していましたが、異なるファイル サイズとスレッド数でも興味深い結果が得られました。

于 2009-05-15T08:12:45.417 に答える