Mejore el rendimiento para enumerar archivos y carpetas usando .NET

 C Programming >> Programación C >  >> Tags >> .NET
Mejore el rendimiento para enumerar archivos y carpetas usando .NET

Esto es (probablemente) lo mejor que se va a poner:

DateTime sixtyLess = DateTime.Now.AddDays(-60);
DirectoryInfo dirInfo = new DirectoryInfo(myBaseDirectory);
FileInfo[] oldFiles = 
    dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories)
           .AsParallel()
           .Where(fi => fi.CreationTime < sixtyLess).ToArray();

Cambios:

  • Hizo los 60 días menos DateTime constante y, por lo tanto, menos carga de CPU.
  • Usado EnumerateFiles .
  • Hizo la consulta paralela.

Debería ejecutarse en una menor cantidad de tiempo (no estoy seguro de cómo mucho más pequeño).

Aquí hay otra solución que puede ser más rápida o más lenta que la primera, depende de los datos:

DateTime sixtyLess = DateTime.Now.AddDays(-60);
DirectoryInfo dirInfo = new DirectoryInfo(myBaseDirectory);
FileInfo[] oldFiles = 
     dirInfo.EnumerateDirectories()
            .AsParallel()
            .SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories)
                                .Where(fi => fi.CreationTime < sixtyLess))
            .ToArray();

Aquí mueve el paralelismo a la enumeración de la carpeta principal. La mayoría de los cambios anteriores también se aplican.


Una alternativa posiblemente más rápida es usar WINAPI FindNextFile . Existe una excelente herramienta de enumeración de directorios más rápida para esto. Que se puede utilizar de la siguiente manera:

HashSet<FileData> GetPast60(string dir)
{
    DateTime retval = DateTime.Now.AddDays(-60);
    HashSet<FileData> oldFiles = new HashSet<FileData>();

    FileData [] files = FastDirectoryEnumerator.GetFiles(dir);
    for (int i=0; i<files.Length; i++)
    {
        if (files[i].LastWriteTime < retval)
        {
            oldFiles.Add(files[i]);
        }
    }    
    return oldFiles;
}

EDITAR

Entonces, según los comentarios a continuación, decidí hacer un punto de referencia de las soluciones sugeridas aquí, así como otras que se me ocurrieron. Fue bastante interesante ver que EnumerateFiles parecía superar a FindNextFile en C# , mientras que EnumerateFiles con AsParallel fue, con mucho, el más rápido, seguido sorprendentemente por el recuento de la línea de comandos . Sin embargo, tenga en cuenta que AsParallel no estaba obteniendo el recuento completo de archivos o faltaban algunos archivos contados por otros, por lo que podría decir que el método del símbolo del sistema es el mejor .

Configuración aplicable:

  • Paquete de servicio de Windows 7 1 x64
  • CPU Intel(R) Core(TM) i5-3210M a 2,50 GHz 2,50 GHz
  • RAM:6GB
  • Objetivo de la plataforma:x64
  • Sin optimización (Nota:la compilación con optimización producirá un rendimiento drásticamente bajo)
  • Permitir código no seguro
  • Comenzar sin depurar

A continuación se muestran tres capturas de pantalla:

He incluido mi código de prueba a continuación:

static void Main(string[] args)
{
    Console.Title = "File Enumeration Performance Comparison";
    Stopwatch watch = new Stopwatch();
    watch.Start();

    var allfiles = GetPast60("C:\\Users\\UserName\\Documents");
    watch.Stop();
    Console.WriteLine("Total time to enumerate using WINAPI =" + watch.ElapsedMilliseconds + "ms.");
    Console.WriteLine("File Count: " + allfiles);

    Stopwatch watch1 = new Stopwatch();
    watch1.Start();

    var allfiles1 = GetPast60Enum("C:\\Users\\UserName\\Documents\\");
    watch1.Stop();
    Console.WriteLine("Total time to enumerate using EnumerateFiles =" + watch1.ElapsedMilliseconds + "ms.");
    Console.WriteLine("File Count: " + allfiles1);

    Stopwatch watch2 = new Stopwatch();
    watch2.Start();

    var allfiles2 = Get1("C:\\Users\\UserName\\Documents\\");
    watch2.Stop();
    Console.WriteLine("Total time to enumerate using Get1 =" + watch2.ElapsedMilliseconds + "ms.");
    Console.WriteLine("File Count: " + allfiles2);


    Stopwatch watch3 = new Stopwatch();
    watch3.Start();

    var allfiles3 = Get2("C:\\Users\\UserName\\Documents\\");
    watch3.Stop();
    Console.WriteLine("Total time to enumerate using Get2 =" + watch3.ElapsedMilliseconds + "ms.");
    Console.WriteLine("File Count: " + allfiles3);

    Stopwatch watch4 = new Stopwatch();
    watch4.Start();

    var allfiles4 = RunCommand(@"dir /a: /b /s C:\Users\UserName\Documents");
    watch4.Stop();
    Console.WriteLine("Total time to enumerate using Command Prompt =" + watch4.ElapsedMilliseconds + "ms.");
    Console.WriteLine("File Count: " + allfiles4);


    Console.WriteLine("Press Any Key to Continue...");
    Console.ReadLine();
}

private static int RunCommand(string command)
{
    var process = new Process()
    {
        StartInfo = new ProcessStartInfo("cmd")
        {
            UseShellExecute = false,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            CreateNoWindow = true,
            Arguments = String.Format("/c \"{0}\"", command),
        }
    };
    int count = 0;
    process.OutputDataReceived += delegate { count++; };
    process.Start();
    process.BeginOutputReadLine();

    process.WaitForExit();
    return count;
}

static int GetPast60Enum(string dir)
{
    return new DirectoryInfo(dir).EnumerateFiles("*.*", SearchOption.AllDirectories).Count();
}

private static int Get2(string myBaseDirectory)
{
    DirectoryInfo dirInfo = new DirectoryInfo(myBaseDirectory);
    return dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories)
               .AsParallel().Count();
}

private static int Get1(string myBaseDirectory)
{
    DirectoryInfo dirInfo = new DirectoryInfo(myBaseDirectory);
    return dirInfo.EnumerateDirectories()
               .AsParallel()
               .SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories))
               .Count() + dirInfo.EnumerateFiles("*.*", SearchOption.TopDirectoryOnly).Count();
}


private static int GetPast60(string dir)
{
    return FastDirectoryEnumerator.GetFiles(dir, "*.*", SearchOption.AllDirectories).Length;
}

NB:Me concentré en el recuento en la fecha de referencia no modificada.


Me doy cuenta de que es muy tarde para la fiesta, pero si alguien más está buscando esto, puede acelerar las cosas en órdenes de magnitud analizando directamente el MFT o FAT del sistema de archivos, esto requiere privilegios de administrador, ya que creo que volverá. todos los archivos independientemente de la seguridad, pero probablemente pueda reducir sus 30 minutos a 30 segundos al menos para la etapa de enumeración.

Una biblioteca para NTFS está aquí https://github.com/LordMike/NtfsLib también hay https://discutils.codeplex.com/ que no he usado personalmente.

Solo usaría estos métodos para el descubrimiento inicial de archivos de más de x días y luego los verificaría individualmente antes de eliminarlos, podría ser excesivo, pero soy cauteloso en ese sentido.