Самый быстрый способ создания файлов в C # - PullRequest
10 голосов
/ 24 января 2012

Я запускаю программу для оценки того, как быстро найти и перебрать все файлы в папке с большим количеством файлов.Самая медленная часть процесса - создание более миллиона файлов.Я сейчас использую довольно наивный метод для создания файлов:

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

Вывод:

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)

Если есть что-то явно лучше этого?Я собираюсь протестировать несколько порядков величиной более 1 миллиона, а создание файлов занимает один день!

Я не пробовал какой-либо параллелизм, пытаясь оптимизировать любые параметры файловой системы или изменятьпорядок создания файла.

Для полноты вот содержание 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();
}

ОБНОВЛЕНИЕ

Попытка распараллелить согласноПредложение StriplingWarrior с использованием Parallel.For().Результаты: около 30 потоков перебивают мой диск, а сеть замедляется!

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

Обнаружено, что изменение FileOptions в FileStream улучшило производительность на ~ 50%.Кажется, я отключил любой кэш записи.

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

Результаты:

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

Другие идеи все еще приветствуются.

Ответы [ 2 ]

9 голосов
/ 24 января 2012

Ваше самое большое узкое место здесь, несомненно, ваш жесткий диск.В некоторых быстрых тестах я смог увидеть некоторые существенные улучшения производительности (но не на порядки), воспользовавшись преимуществом параллелизма:

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

Интересно, что на моей машине, по крайней мере, SSD не выглядитчтобы изменить ситуацию к этой операции.

  • На моем жестком диске вышеуказанный код создает 100 000 файлов примерно за 31 секунду.
  • На моем SDD вышеуказанный код создает 100 000 файлов впримерно 33 секунды.
3 голосов
/ 25 января 2012

Самый быстрый способ, который я нашел, был простой цикл вокруг File.Create():

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

Что эквивалентно (что я на самом деле использую в коде):

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

А если вы действительно хотите что-то записать в файл:

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

Вещи, которые не помогают:

  • Параллелизм в форме Parallel.ForEach() или Parallel.For(). Это приводит к замедлению сети, которое ухудшается с увеличением числа потоков.
  • По данным StriplingWarrior, SSD. Сам еще не проверял (пока), но я полагаю, что это может быть из-за того, что так много маленьких записей.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...