una obtener net manipular listar guardar directorios directorio desde crear carpeta buscar asp archivos archivo abrir c# windows winforms winapi filesystemobject

obtener - manipular archivos y directorios en c#



Cálculo del tamaño del archivo del directorio: ¿cómo hacerlo más rápido? (8)

Utilizando C #, estoy encontrando el tamaño total de un directorio. La lógica es de esta manera: obtener los archivos dentro de la carpeta. Resumir el tamaño total. Encuentra si hay subdirectorios. Entonces haz una búsqueda recursiva.

También intenté hacer otra forma de hacerlo: usando FSO ( obj.GetFolder(path).Size ). No hay mucha diferencia en el tiempo en estos dos enfoques.

Ahora el problema es que tengo decenas de miles de archivos en una carpeta en particular y me está costando como mínimo 2 minutos encontrar el tamaño de la carpeta. Además, si vuelvo a ejecutar el programa, sucede muy rápidamente (5 segundos). Creo que las ventanas están almacenando en caché los tamaños de archivo.

¿Hay alguna manera de reducir el tiempo empleado cuando ejecuto el programa por primera vez?


Basado en la respuesta de spookycoder, encontré esta variación (utilizando DirectoryInfo ) al menos 2 veces más rápido (y hasta 10 veces más rápido en estructuras de carpetas complejas):

public static long CalcDirSize(string sourceDir, bool recurse = true) { return _CalcDirSize(new DirectoryInfo(sourceDir), recurse); } private static long _CalcDirSize(DirectoryInfo di, bool recurse = true) { long size = 0; FileInfo[] fiEntries = di.GetFiles(); foreach (var fiEntry in fiEntries) { Interlocked.Add(ref size, fiEntry.Length); } if (recurse) { DirectoryInfo[] diEntries = di.GetDirectories("*.*", SearchOption.TopDirectoryOnly); System.Threading.Tasks.Parallel.For<long>(0, diEntries.Length, () => 0, (i, loop, subtotal) => { if ((diEntries[i].Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) return 0; subtotal += __CalcDirSize(diEntries[i], true); return subtotal; }, (x) => Interlocked.Add(ref size, x) ); } return size; }


Con decenas de miles de archivos, no vas a ganar con un asalto frontal. Debes intentar ser un poco más creativo con la solución. Con tantos archivos, probablemente podría encontrar que en el tiempo que lleva calcular el tamaño, los archivos han cambiado y sus datos ya están equivocados.

Por lo tanto, necesitas mover la carga a otro lugar. Para mí, la respuesta sería usar System.IO.FileSystemWatcher y escribir algún código que supervise el directorio y actualice un índice.

Debería tomar poco tiempo escribir un servicio de Windows que pueda configurarse para monitorear un conjunto de directorios y escribir los resultados en un archivo de salida compartido. Puede hacer que el servicio vuelva a calcular los tamaños de los archivos en el inicio, pero luego solo supervise los cambios cada vez que el evento System.IO.FileSystemWatcher dispare un evento Crear / Eliminar / Cambiado. El beneficio de monitorear el directorio es que solo está interesado en pequeños cambios, lo que significa que sus cifras tienen una mayor probabilidad de ser correctas (¡recuerde que todos los datos están obsoletos!)

Entonces, lo único que debe tener en cuenta es que tendría varios recursos para intentar acceder al archivo de salida resultante. Así que asegúrate de tenerlo en cuenta.


El rendimiento sufrirá el uso de cualquier método al escanear una carpeta con decenas de miles de archivos.

  • El uso de las funciones FindFirstFile ... y FindNextFile ... de la API de Windows proporciona el acceso más rápido.

  • Debido a la sobrecarga de cálculo, incluso si utiliza las funciones de la API de Windows, el rendimiento no aumentará. El marco ya incluye estas funciones de API, por lo que no tiene sentido hacerlo usted mismo.

  • La forma en que maneje los resultados para cualquier método de acceso a archivos determina el rendimiento de su aplicación. Por ejemplo, incluso si usa las funciones de la API de Windows, la actualización de un cuadro de lista es donde el rendimiento se verá afectado.

  • No se puede comparar la velocidad de ejecución con el Explorador de Windows. Desde mi experimentación, creo que el Explorador de Windows lee directamente de la tabla de asignación de archivos en muchos casos.

  • Sé que el acceso más rápido al sistema de archivos es el comando DIR . No se puede comparar el rendimiento con este comando. Definitivamente se lee directamente de la tabla de asignación de archivos (posiblemente usando BIOS).

  • Sí, el sistema operativo almacena en caché el acceso a los archivos.

Sugerencias

  • Me pregunto si BackupRead te ayudaría en tu caso.

  • ¿Qué pasa si desembolsa en DIR y captura y luego analiza su salida? (Realmente no estás analizando porque cada línea DIR tiene un ancho fijo, por lo que es solo una cuestión de llamar subcadena).

  • ¿Qué pasa si desembolsa a DIR /B > NULL en un subproceso de fondo y luego ejecuta su programa? Mientras se ejecuta DIR, se beneficiará del acceso al archivo en caché.


La respuesta corta es no. La forma en que Windows podría hacer que el cálculo del tamaño del directorio sea más rápido sería actualizar el tamaño del directorio y todos los tamaños del directorio principal en cada escritura de archivo. Sin embargo, eso haría que el archivo escriba una operación más lenta. Ya que es mucho más común hacer escrituras de archivos que leer tamaños de directorios, es una compensación razonable.

No estoy seguro de qué problema exacto se está resolviendo, pero si se trata de la supervisión del sistema de archivos, puede valer la pena consultar: http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx


Los discos duros son una bestia interesante: el acceso secuencial (leer un gran archivo contiguo, por ejemplo) es super zippy, figura 80 megabytes / seg. Sin embargo, el acceso aleatorio es muy lento. esto es en lo que se está topando: las repeticiones en las carpetas no leen muchos datos (en términos de cantidad), pero requerirán muchas lecturas aleatorias. La razón por la que estás viendo a Zippy Perf en la segunda ronda es porque la MFT todavía está en la memoria RAM (tienes razón en el pensamiento de almacenamiento en caché)

El mejor mecanismo que he visto para lograr esto es escanear la MFT usted mismo. La idea es que lea y analice la MFT en un paso lineal y cree la información que necesita a medida que avanza. El resultado final será algo mucho más cercano a 15 segundos en una HD que está muy llena.

Algunas buenas lecturas: NTFSInfo.exe - http://technet.microsoft.com/en-us/sysinternals/bb897424.aspx Windows Internals - http://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-PRO-Developer/dp/0735625301/ref=sr_1_1?ie=UTF8&s=books&qid=1277085832&sr=8-1

FWIW: este método es muy complicado, ya que realmente no hay una buena forma de hacer esto en Windows (o en cualquier sistema operativo que conozca). El problema es que el hecho de averiguar qué carpetas / archivos son necesarios requiere mucha atención. Movimiento en el disco. Sería muy difícil para Microsoft crear una solución general al problema que describe.


No creo que cambie mucho, pero podría ir un poco más rápido si utiliza las funciones de API FindFirstFile y NextFile para hacerlo.

Sin embargo, no creo que haya una manera realmente rápida de hacerlo. Para propósitos de comparación, puede intentar hacer dir /a /x /s > dirlist.txt y listar el directorio en el Explorador de Windows para ver qué tan rápido son, pero creo que serán similares a FindFirstFile .

PInvoke tiene una muestra de cómo usar la API.


Renuncié a las implementaciones .NET (por razones de rendimiento) y usé la función nativa GetFileAttributesEx (...)

Prueba esto:

[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); }

Ahora simplemente haga lo siguiente:

WIN32_FILE_ATTRIBUTE_DATA data; if(NativeMethods.GetFileAttributesEx("[your path]", GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out data)) { long size = (data.fileSizeHigh << 32) & data.fileSizeLow; }


Si jugueteaba un poco con él, intentaba paralelizarlo y, sorprendentemente, aceleraba aquí en mi máquina (hasta 3 veces en un quadcore), no sé si es válido en todos los casos, pero pruébalo. ..

Código .NET4.0 (o uso 3.5 con TaskParallelLibrary)

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; }