2012-01-23 11 views
10

Estoy ejecutando un programa para comparar qué tan rápido encontrar e iterar sobre todos los archivos en una carpeta con un gran número de archivos. La parte más lenta del proceso es crear el millón de archivos más. Estoy usando un método bastante ingenua para crear los archivos en la actualidadLa forma más rápida de crear archivos en C#

Console.Write("Creating {0:N0} file(s) of size {1:N0} bytes... ", 
    options.FileCount, options.FileSize); 
var createTimer = Stopwatch.StartNew(); 
var fileNames = new List<string>(); 
for (long i = 0; i < options.FileCount; i++) 
{ 
    var filename = Path.Combine(options.Directory.FullName, 
         CreateFilename(i, options.FileCount)); 
    using (var file = new FileStream(filename, FileMode.CreateNew, 
         FileAccess.Write, FileShare.None, 4096, 
         FileOptions.WriteThrough)) 
    { 
     // I have an option to write some data to files, but it's not being used. 
     // That's why there's a using here. 
    } 
    fileNames.Add(filename); 
} 
createTimer.Stop(); 
Console.WriteLine("Done."); 

// Other code appears here..... 

Console.WriteLine("Time to CreateFiles: {0:N3}sec ({1:N2} files/sec, 1 in {2:N4}ms)" 
     , createTimer.Elapsed.TotalSeconds 
     , (double)total/createTimer.Elapsed.TotalSeconds 
     , createTimer.Elapsed.TotalMilliseconds/(double)options.FileCount); 

Salida:

Creating 1,000,000 file(s) of size 0 bytes... Done. 
Time to CreateFiles: 9,182.283sec (1,089.05 files/sec, 1 in 9.1823ms) 

Si hay algo obviamente mejor que esto? Estoy buscando para probar varios órdenes de magnitud superior a 1 millón, y se necesita un día para crear los archivos!

No he probado ningún tipo de paralelismo, tratando de optimizar cualquier opción de sistema de archivos o cambiando el orden de creación de archivos.

Para completar, aquí está el contenido de CreateFilename():

public static string CreateFilename(long i, long totalFiles) 
{ 
    if (totalFiles < 0) 
     throw new ArgumentOutOfRangeException("totalFiles", 
      totalFiles, "totalFiles must be positive"); 

    // This tries to keep filenames to the 8.3 format as much as possible. 
    if (totalFiles < 99999999) 
     // No extension. 
     return String.Format("{0:00000000}", i); 
    else if (totalFiles >= 100000000 && totalFiles < 9999999999) 
    { 
     // Extend numbers into extension. 
     long rem = 0; 
     long div = Math.DivRem(i, 1000, out rem); 
     return String.Format("{0:00000000}", div) + "." + 
      String.Format("{0:000}", rem); 
    } 
    else 
     // Doesn't fit in 8.3, so just tostring the long. 
     return i.ToString(); 
} 

ACTUALIZACIÓN

Intentó paralelizar según la sugerencia de StriplingWarrior usando Parallel.For(). Resultados: alrededor de 30 hilos golpeando mi disco y una red más lenta!

 var fileNames = new ConcurrentBag<string>(); 
     var opts = new ParallelOptions(); 
     opts.MaxDegreeOfParallelism = 1;  // 1 thread turns out to be fastest. 
     Parallel.For(0L, options.FileCount, opts, 
      () => new { Files = new List<string>() }, 
      (i, parState, state) => 
      { 
       var filename = Path.Combine(options.Directory.FullName, 
            CreateFilename(i, options.FileCount)); 
       using (var file = new FileStream(filename, FileMode.CreateNew 
            , FileAccess.Write, FileShare.None 
            , 4096, FileOptions.WriteThrough)) 
       { 
       } 
       fileNames.Add(filename); 
       return state; 
      }, 
      state => 
      { 
       foreach (var f in state.Files) 
       { 
        fileNames.Add(f); 
       } 
      }); 
     createTimer.Stop(); 
     Console.WriteLine("Done."); 

encontrado que el cambio de la FileOptions en el FileStream mejorado perf por ~ 50%. Parece que estaba apagando cualquier caché de escritura.

new FileStream(filename, FileMode.CreateNew, 
       FileAccess.Write, FileShare.None, 
       4096, FileOptions.None) 

Resultados:

Creating 10,000 file(s) of size 0 bytes... Done. 
Time to CreateFiles: 12.390sec (8,071.05 files/sec, 1 in 1.2390ms) 

Otras ideas todavía dan la bienvenida.

+5

¿Tiene que escribirlos todos en un único directorio? Si puedes dividirlo para decir 1000 archivos en 1000 directorios, las cosas pueden ser más rápidas. – Oded

+0

¿Qué hay de usar C? Por cierto, creo que habrá limitaciones si usa HDD. SSD puede ayudar a acelerar las cosas un poco. – user482594

+3

Por lo que vale, este es casi seguro un límite de su disco/sistema de archivos particular, no C#. La solución SSD ayudaría, con seguridad. –

Respuesta

3

La forma más rápida que encontré fue un simple bucle alrededor File.Create():

IEnumerable filenames = GetFilenames(); 
foreach (var filename in filenames) 
{ 
    File.Create(filename); 
} 

lo que equivale a (lo que en realidad estoy usando en el código):

IEnumerable filenames= GetFilenames(); 
foreach (var filename in filenames) 
{ 
    new FileStream(filename, FileMode.CreateNew, 
      FileAccess.Write, FileShare.None, 
      4096, FileOptions.None) 
} 

Y si De hecho, quiero escribir algo en el archivo:

IEnumerable filenames= GetFilenames(); 
foreach (var filename in filenames) 
{ 
    using (var fs = new FileStream(filename, FileMode.CreateNew, 
      FileAccess.Write, FileShare.None, 
      4096, FileOptions.None)) 
    { 
     // Write something to your file. 
    } 
} 

cosas que no parecen ayudar:

  • paralelismo en la forma de Parallel.ForEach() o Parallel.For(). Esto produce una desaceleración neta que empeora a medida que aumenta el número de subprocesos.
  • De acuerdo con StriplingWarrior, una SSD. Aún no me he probado (todavía), pero especulo que esto puede deberse a que hay muchas escrituras pequeñas.
8

Su mayor cuello de botella aquí es, sin duda, su disco duro. En algunas pruebas rápida, pude ver algunas mejoras significativas en el rendimiento (pero no órdenes de magnitud) mediante el aprovechamiento del paralelismo:

Parallel.For(1, 10000, 
    i => File.Create(Path.Combine(path, i.ToString()))); 

Curiosamente, en mi máquina, al menos, un SSD no parece hacer una gran diferencia para esta operación

  • En mi HDD, el código anterior crea 100.000 archivos en aproximadamente 31 segundos.
  • En mi SDD, el código anterior crea 100.000 archivos en aproximadamente 33 segundos.
+0

Hmmm ... No estoy obteniendo ninguna aceleración usando 'Parallel.For()'. Pero tu código se ejecuta más rápido. Debe ser algo más ... – ligos

+1

¡Ah! 'File.Create()' es un orden de magnitud más rápido que crear un nuevo 'FileStream'. – ligos

+0

@ligos: me alegra saber que eso ayudó. Extraño cómo son las cosas simples e inesperadas las que a veces marcan la diferencia. :-) – StriplingWarrior

Cuestiones relacionadas