20

C# を使用して、ディレクトリの合計サイズを見つけています。ロジックは次のとおりです。フォルダー内のファイルを取得します。全体のサイズを合計します。サブディレクトリがあるかどうかを確認します。次に、再帰検索を実行します。

私もこれを行う別の方法を試しました: FSO ( obj.GetFolder(path).Size) を使用します。これらの両方のアプローチで時間に大きな違いはありません。

問題は、特定のフォルダーに何万ものファイルがあり、フォルダーのサイズを見つけるのに少なくとも2分かかることです。また、プログラムを再度実行すると、非常に速く (5 秒) 実行されます。ウィンドウがファイルサイズをキャッシュしていると思います。

プログラムを初めて実行するときにかかる時間を短縮する方法はありますか??

4

8 に答える 8

36

しばらくいじって、並列化しようとすると、驚くべきことに、私のマシンではここで高速化されました (クアッドコアでは最大 3 倍)。すべての場合に有効かどうかはわかりませんが、試してみてください。 ..

.NET4.0 コード (または TaskParallelLibrary で 3.5 を使用)

    private static long DirSize(string sourceDir, bool recurse)
    {
        long size = 0;
        string[] fileEntries = Directory.GetFiles(sourceDir);

        foreach (string fileName in fileEntries)
        {
            Interlocked.Add(ref size, (new FileInfo(fileName)).Length);
        }

        if (recurse)
        {
            string[] subdirEntries = Directory.GetDirectories(sourceDir);

            Parallel.For<long>(0, subdirEntries.Length, () => 0, (i, loop, subtotal) =>
            {
                if ((File.GetAttributes(subdirEntries[i]) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
                {
                    subtotal += DirSize(subdirEntries[i], true);
                    return subtotal;
                }
                return 0;
            },
                (x) => Interlocked.Add(ref size, x)
            );
        }
        return size;
    }
于 2010-06-05T17:02:34.413 に答える
10

ハードディスクは興味深い獣です。シーケンシャル アクセス (たとえば、大きな連続したファイルの読み取り) は非常に高速で、80 メガバイト/秒です。ただし、ランダム アクセスは非常に低速です。これはあなたがぶつかっているものです - フォルダへの再帰は(量の点で)多くのデータを読み取ることはありませんが、多くのランダムな読み取りが必要になります. 2回目のパフォーマンスがジッピーである理由は、MFTがまだRAMにあるためです(キャッシュの考えは正しいです)

これを達成するために私が見た最良のメカニズムは、MFT を自分でスキャンすることです。アイデアは、必要な情報を構築する 1 つの線形パスで MFT を読み取って解析することです。最終結果は、非常にいっぱいの HD で 15 秒近くになります。

いくつかの良い読書: NTFSInfo.exe - http://technet.microsoft.com/en-us/sysinternals/bb897424.aspx Windows 内部 - http://www.amazon.com/Windows%C2%AE-Internals-Inclusive- Windows-PRO-Developer/dp/0735625301/ref=sr_1_1?ie=UTF8&s=books&qid=1277085832&sr=8-1

FWIW:Windows(または私が知っているOS)でこれを行うための優れた方法がないため、この方法は非常に複雑です-問題は、どのフォルダー/ファイルが必要かを理解するという行為に多くの頭が必要なことですディスク上の動き。あなたが説明した問題に対する一般的な解決策をMicrosoftが構築するのは非常に難しいでしょう.

于 2010-06-21T02:05:52.983 に答える
7

簡単な答えはノーです。Windowsがディレクトリサイズの計算を高速化する方法は、各ファイルの書き込み時にディレクトリサイズとすべての親ディレクトリサイズを更新することです。ただし、ファイルの書き込みが遅くなります。ディレクトリサイズの読み取りよりもファイルの書き込みを行う方がはるかに一般的であるため、これは妥当なトレードオフです。

正確な問題が解決されているかどうかはわかりませんが、ファイルシステムの監視の場合は、チェックする価値があるかもしれません:http: //msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx

于 2010-06-05T06:54:59.993 に答える
2

何万ものファイルが含まれるフォルダをスキャンする場合、どのような方法を使用してもパフォーマンスが低下します。

  • WindowsAPIのFindFirstFile...およびFindNextFile...関数を使用すると、最速のアクセスが提供されます。

  • マーシャリングのオーバーヘッドにより、Windows API関数を使用しても、パフォーマンスは向上しません。フレームワークはすでにこれらのAPI関数をラップしているので、自分でそれを行う意味はありません。

  • ファイルアクセス方法の結果をどのように処理するかによって、アプリケーションのパフォーマンスが決まります。たとえば、Windows API関数を使用している場合でも、リストボックスを更新するとパフォーマンスが低下します。

  • 実行速度をWindowsエクスプローラーと比較することはできません。私の実験から、Windowsエクスプローラーは多くの場合file-allocation-tableから直接読み取ると思います。

  • ファイルシステムへの最速のアクセスはコマンドであることを私は知っていますDIR。パフォーマンスをこのコマンドと比較することはできません。それは間違いなくfile-allocation-tableから直接読み取ります(おそらくBIOSを使用します)。

  • はい、オペレーティングシステムはファイルアクセスをキャッシュします。

提案

  • BackupReadあなたの場合に役立つでしょうか?

  • DIRにシェルアウトしてキャプチャし、その出力を解析するとどうなりますか?(各DIR行は固定幅であるため、実際には解析していません。したがって、サブストリングを呼び出すだけです。)

  • DIR /B > NULLバックグラウンドスレッドでシェルアウトしてからプログラムを実行するとどうなりますか?DIRの実行中は、キャッシュされたファイルアクセスの恩恵を受けることができます。

于 2010-06-16T02:22:59.773 に答える
1

あまり変わらないと思いますが、API関数を使ってやれば少し速くなるかもしれませFindFirstFileNextFile

しかし、それを行うための本当に迅速な方法はないと思います。比較のためdir /a /x /s > dirlist.txtに、Windowsエクスプローラーでディレクトリを一覧表示して、それらの速度を確認することもできますが、これらはに似ていると思いますFindFirstFile

PInvokeには、APIの使用方法のサンプルがあります。

于 2010-06-05T06:55:54.143 に答える
0

(パフォーマンス上の理由で).NETの実装をあきらめ、ネイティブ関数GetFileAttributesEx(...)を使用しました

これを試して:

[StructLayout(LayoutKind.Sequential)]
public struct WIN32_FILE_ATTRIBUTE_DATA
{
    public uint fileAttributes;
    public System.Runtime.InteropServices.ComTypes.FILETIME creationTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME lastAccessTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME lastWriteTime;
    public uint fileSizeHigh;
    public uint fileSizeLow;
}

public enum GET_FILEEX_INFO_LEVELS
{
    GetFileExInfoStandard,
    GetFileExMaxInfoLevel
}

public class NativeMethods {
    [DllImport("KERNEL32.dll", CharSet = CharSet.Auto)]
    public static extern bool GetFileAttributesEx(string path, GET_FILEEX_INFO_LEVELS  level, out WIN32_FILE_ATTRIBUTE_DATA data);

}

ここで、次のようにします。

WIN32_FILE_ATTRIBUTE_DATA data;
if(NativeMethods.GetFileAttributesEx("[your path]", GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out data)) {

     long size = (data.fileSizeHigh << 32) & data.fileSizeLow;
}
于 2010-06-21T10:05:06.330 に答える
0

何万ものファイルがあるので、正面からの攻撃で勝つことはできません。このソリューションでは、もう少しクリエイティブになるようにする必要があります。その数のファイルを使用すると、サイズの計算にかかる時間内にファイルが変更され、データがすでに間違っていることに気付く可能性があります。

したがって、負荷を別の場所に移動する必要があります。私にとっての答えはSystem.IO.FileSystemWatcher、ディレクトリを監視してインデックスを更新するコードを使用して作成することです。

一連のディレクトリを監視し、結果を共有出力ファイルに書き込むように構成できるWindowsサービスを作成するには、短時間で済みます。起動時にサービスにファイルサイズを再計算させることができますが、Create / Delete/Changedイベントがによって発生するたびに変更を監視するだけSystem.IO.FileSystemWatcherです。ディレクトリを監視する利点は、小さな変更のみに関心があることです。つまり、数値が正しい可能性が高くなります(すべてのデータが古くなっていることを忘れないでください)。

次に、注意すべき唯一のことは、結果の出力ファイルにアクセスしようとする複数のリソースがあることです。したがって、それを考慮に入れてください。

于 2010-06-17T11:06:01.953 に答える