2012-01-23 13 views
10

Używam programu do testowania sposobu szybkiego znajdowania i iterowania wszystkich plików w folderze z dużą liczbą plików. Najwolniejsza część procesu polega na utworzeniu ponad miliona plików. Używam metody dość naiwny, aby tworzyć pliki w tej chwili:Najszybszy sposób na tworzenie plików w języku 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); 

wyjściowa:

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) 

Jeśli oczywiście coś lepszego niż to? Próbuję przetestować kilka rzędów wielkości większych niż 1 milion, a tworzenie plików trwa dzień!

Nie próbowałem żadnego paralelizmu, próbując zoptymalizować dowolne opcje systemu plików lub zmieniając kolejność tworzenia plików.

Dla kompletności, oto treść 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(); 
} 

UPDATE

Próbował parallelise zgodnie z sugestią StriplingWarrior Korzystanie Parallel.For(). Rezultaty: około 30 wątków zrzuconych na dysk i spowolnienie sieci!

 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."); 

Stwierdzono, że zmiana FileOptions w FileStream poprawiła perf o ~ 50%. Wygląda na to, że wyłączałem pamięć podręczną zapisu.

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

Wyniki:

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

Inne pomysły nadal mile widziane.

+5

Czy musisz zapisać je wszystkie w jednym katalogu? Jeśli możesz go złamać, aby powiedzieć 1000 plików w 1000 katalogów, może się zdarzyć, że będzie szybciej. – Oded

+0

Co powiesz na używanie C? Przy okazji, myślę, że będą ograniczenia, jeśli używasz HDD. Dysk SSD może nieco przyspieszyć działanie. – user482594

+3

Co jest warte, prawie na pewno jest to limit określonego dysku/systemu plików, a nie C#. Rozwiązanie SSD mogłoby na pewno pomóc. –

Odpowiedz

3

Najszybszym sposobem znalazłem była prosta pętla wokół File.Create():

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

co jest równoważne (co jestem w rzeczywistości za pomocą kodu):

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

a jeśli faktycznie chcesz napisać coś do pliku:

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

rzeczy, które nie wydają się pomóc:

  • Równoległość w postaci Parallel.ForEach() lub Parallel.For(). Powoduje to spowolnienie sieci, które pogarsza się wraz ze wzrostem liczby wątków.
  • Według StriplingWarrior, SSD. Nie testowałem siebie (jeszcze), ale spekuluję, że może tak być, ponieważ jest tak wiele małych pism.
8

Twoim największym wąskim gardłem jest niewątpliwie Twój dysk twardy. W pewnym szybkiego testowania, udało mi się zobaczyć kilka istotnych ulepszeń wydajności (ale nie o rzędy wielkości), korzystając z równoległości:

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

Co ciekawe, na moim komputerze przynajmniej SSD nie wydaje się, aby duża różnica w tej operacji.

  • Powyższy kod tworzy 100 000 plików w ciągu około 31 sekund.
  • Na moim SDD powyższy kod tworzy 100 000 plików w około 33 sekund.
+0

Hmmm ... Nie dostaję żadnego przyspieszenia od używania 'Parallel.For()'. Ale twój kod działa szybciej. To musi być coś innego ... – ligos

+1

Ah !! 'File.Create()' jest o rząd wielkości szybszy niż tworzenie nowego 'FileStream'. – ligos

+0

@ligos: Cieszę się, że to pomogło. Dziwne, jak to jest proste, nieoczekiwane rzeczy, które czasem robią różnicę. :-) – StriplingWarrior

Powiązane problemy