0

ファイルの読み取りと書き込みを頻繁に行うアプリケーション(カスタム形式)があり、直接アンマネージコードを使用してパフォーマンスを向上させるように言われました。実際のアプリケーションで試す前に、パフォーマンスの向上がどのようになるかを確認するために小さなテストを行いましたが、驚いたことに、アンマネージバージョンは単純なファイルストリームを使用するよりも8倍遅いようです。

管理対象関数は次のとおりです。

    private int length = 100000;
    private TimeSpan tspan;

    private void UsingManagedFileHandle()
    {
        DateTime initialTime = DateTime.Now;

        using (FileStream fileStream = new FileStream("data2.txt", FileMode.Create, FileAccess.ReadWrite))
        {
            string line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890123";
            byte[] bytes = Encoding.Unicode.GetBytes(line);

            for (int i = 0; i < length; i++)
            {
                fileStream.Write(bytes, 0, bytes.Length);
            }

            fileStream.Close();
        }

        this.tspan = DateTime.Now.Subtract(initialTime);
        label2.Text = "" + this.tspan.TotalMilliseconds + " Milliseconds";
    }

管理されていない方法は次のとおりです。

    public void UsingAnUnmanagedFileHandle()
    {

        DateTime initialTime;
        IntPtr hFile;

        hFile = IntPtr.Zero;

        hFile = FileInteropFunctions.CreateFile("data1.txt",
            FileInteropFunctions.GENERIC_WRITE | FileInteropFunctions.GENERIC_READ,
            FileInteropFunctions.FILE_SHARE_WRITE,
            IntPtr.Zero,
            FileInteropFunctions.CREATE_ALWAYS,
            FileInteropFunctions.FILE_ATTRIBUTE_NORMAL, 
            0);

        uint lpNumberOfBytesWritten = 0;

        initialTime = DateTime.Now;

        if (hFile.ToInt64() > 0)
        {
            string line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890123"; 
            byte[] bytes = Encoding.Unicode.GetBytes(line);
            uint bytesLen = (uint)bytes.Length;

            for (int i = 0; i < length; i++)
            {
                FileInteropFunctions.WriteFile(hFile,
                        bytes,
                        bytesLen,
                        out lpNumberOfBytesWritten,
                        IntPtr.Zero);
            }

            FileInteropFunctions.CloseHandle(hFile);

            this.tspan = DateTime.Now.Subtract(initialTime);
            label1.Text = "" + this.tspan.TotalMilliseconds + " Milliseconds";

        }
        else
            label1.Text = "Error";

    }

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool CloseHandle(IntPtr hObject);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern unsafe IntPtr CreateFile(
        String lpFileName,              // Filename
        uint dwDesiredAccess,              // Access mode
        uint dwShareMode,              // Share mode
        IntPtr attr,                   // Security Descriptor
        uint dwCreationDisposition,           // How to create
        uint dwFlagsAndAttributes,           // File attributes
        uint hTemplateFile);               // Handle to template file


    [DllImport("kernel32.dll")]
    public static extern unsafe int WriteFile(IntPtr hFile,
        // byte[] lpBuffer,
        [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, // also tried this.
        uint nNumberOfBytesToWrite, 
        out uint lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

FileStreamを使用した反復は、私のコンピューターで約70ミリ秒かかります。WriteFileを使用するものは約550msかかります。

私は数回、数回の反復でテストしましたが、パフォーマンスの違いは一貫しています。

アンマネージコードがマネージコードよりも遅い理由がわかりません。

編集

説明ありがとうございます。FileStreamを実行している「魔法のような」何かがあると思いましたが、あなたはそれをとてもよく説明しました。ですから、この部分でパフォーマンスを上げる簡単な方法はないことを私は知っています。スピードを上げるための他の簡単な方法について意見を求めたいと思います。このファイルは実際のアプリケーションではランダムアクセスであり、サイズは1MBから1GBの範囲である可能性があります。

4

3 に答える 3

3

アンマネージ呼び出しは、FileStream がバッファリングされている間、できるだけ早くデータをディスクに書き込みます (つまり、ほとんどの操作をメモリ内で実行し、基になるアンマネージ呼び出しを呼び出す頻度を大幅に減らす必要があります)。

パフォーマンスをさらに微調整したい場合に、バッファ サイズを制御できるFileStream のコンストラクタがあります。

于 2012-12-29T07:13:20.060 に答える
2

FileStream は、CreateFile/WriteFile のラッパーです。それは賢い人たちの束によって書かれています。したがって、なぜあなたのほうが速いと思うのか、論理的な説明はまったくありません:P.

既に述べたように、FileStream はおそらく WriteFile() を呼び出す前に余分なバッファリングを行い、アンマネージ メソッドの呼び出しを最小限に抑えます。これは重要です。必要な場合にのみ、アンマネージド コールを実行してください。かかる。通常、バッファ サイズはディスク セクタ サイズの倍数です。さまざまなサイズを試すことができますが、これは OS に依存し、他のコンピューターで別の結果が得られる可能性が最も高いです。

しかし、WriteFile() が内部バッファリングも行うことを知っておくことも重要です。WriteFile() を呼び出してファイルに書き込むようなものではありません。時間になるとHDDにフラッシュされます。

不必要な byte[] マーシャリングが行われていると思います。たとえば、WriteFile() を呼び出すと、システムはバッファのコピーを作成します。unsafe() キーワードと少しのハッキングで回避できるはずです。

FileStream(afaik) を介してアクセスできない FILE_FLAG_SEQUENTIAL_SCAN もあり、ファイルの書き込み/読み取りを順次のみ行うことをシステムに通知する必要があります。これにより、理論的にはパフォーマンスが向上する可能性があります。

于 2012-12-29T07:49:08.520 に答える
1

違いは、WriteFileへの書き込みは同期してFileStreamいないのに対し、 への呼び出しは同期しているためです。

デフォルトでCreateFileは、 は同期ファイル ハンドルを作成するためWriteFile、データが書き込まれるまで呼び出しは戻りません。呼び出しに追加FILE_FLAG_OVERLAPPEDすると、管理されていない実装は、管理されている実装とほぼ同じ時間がかかります。CreateFile

定義の同期および非同期 I/O ハンドルセクションのCreateFileドキュメントを参照してください。

于 2012-12-29T07:42:50.347 に答える