2013-09-23 4 views
5

У меня есть консольное приложение, которое займет около 625 дней. Если есть способ сделать это быстрее.Ускоренный метод перемещения файлов, отличный от File.Move

Прежде всего, я работаю в каталоге, в котором есть около 4 000 000 файлов, если не больше. Я работаю в базе данных, которая содержит строку для каждого файла, а затем некоторую.

Теперь работа с SQL относительно быстро, узкое место, когда я использую File.Move(), каждый ход занимает 18 секунд.

Есть ли более быстрый способ, чем File.Move()?

Это узкое место:

File.Move(Path.Combine(location, fileName), Path.Combine(rootDir, fileYear, fileMonth, fileName)); 

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

Я могу показать другой код, если это необходимо, но на самом деле вышесказанное является единственным текущим узким местом.

+0

Если вы используете базу данных в любом случае , зачем вам всего 4 000 000 файлов? –

+0

@TimSchmelter Первоначально они разработали его. В базе данных хранится некоторая информация из файла, единственной частью, которую мне нужно обновить, является столбец «Местоположение». Эта колонка - это то, что говорит прикладной программе, которую они используют, где находится документ, чтобы открыть его. –

+0

Если каждый шаг занимает 18 секунд, то что-то еще очень * неправильно, и это, вероятно, не ваше использование API. – cdhowie

ответ

10

Оказывается, переход от File.Move к настройке FileInfo и использованию .MoveTo значительно увеличивает скорость.

Он будет работать примерно через 35 дней, а не 625 дней.

FileInfo fileinfo = new FileInfo(Path.Combine(location, fileName)); 
fileinfo.MoveTo(Path.Combine(rootDir, fileYear, fileMonth, fileName)); 
+0

Это хорошая информация. Кажется, странно, что так будет. Мне, возможно, придется исследовать, почему это так. –

+0

@ JimMischel Да, я тестировал весь этот день, скорость была последовательным изменением с этим большим количеством файлов. Все, что я мог найти, это то, что File.Move проверяет разрешение/безопасность для каждого вызова, где fileInfo.MoveTo() проверяет его только один раз. Если вы найдете что-нибудь еще, я бы с удовольствием узнал. –

+0

Очень странно. Я не нашел возможности повышения скорости: 10529 мс (32824028 tiks) Directory.Move, 13358 ms (41642456 tiks) new FileInfo(). Move, 10926 ms (34061807 tiks) File.Move(). Это для 16385 файлов –

2

18 секунд на самом деле необычно. NTFS не работает хорошо, когда у вас много файлов в одном каталоге. Когда вы запрашиваете файл, он должен выполнить линейный поиск своей структуры данных каталога. С 1000 файлов это не займет слишком много времени. С 10 000 файлов вы замечаете это. С 4 миллионами файлов. , , да, это требует времени.

Возможно, вы можете сделать это еще быстрее, если предварительно загрузите все записи каталога в память. Затем вместо вызова конструктора FileInfo для каждого файла вы просто просматриваете его в своем словаре.

Что-то вроде:

var dirInfo = new DirectoryInfo(path); 
// get list of all files 
var files = dirInfo.GetFileSystemInfos(); 
var cache = new Dictionary<string, FileSystemInfo>(); 
foreach (var f in files) 
{ 
    cache.Add(f.FullName, f); 
} 

Теперь, когда вы получаете имя из базы данных, вы можете просто посмотреть его в словаре. Это может быть намного быстрее, чем пытаться получить его с диска каждый раз.

+0

Я боюсь протестировать это, так как ему потребуется загрузить 4 миллиона файлов в каталог, прежде чем он сможет начать любую работу по их перемещению. И тогда, когда они будут в словаре, мне все равно нужно будет выполнить File.Move или fileinfo.MoveTo() в файле, если они не ошибаются? –

+0

@JamesWilson: Да, вам все равно нужно будет 'fileinfo.MoveTo()'. Идея состоит в том, что предварительная загрузка всех записей приведет к тому, что вам придется искать их по очереди. Является ли 4 миллиона записей проблемой памяти, я не знаю. Я также не знаю, сколько времени потребуется для загрузки, хотя я подозреваю, что это будет намного меньше часа. Будет ли результат быстрее, чем ваши 35 дней, я не знаю точно. –

2

Вы можете перемещать файлы параллельно, а также с помощью Directory.EnumerateFiles дает ленивым загруженный список файлов (из-я, конечно, не проверял его с 4000000 файлов):

var numberOfConcurrentMoves = 2; 
var moves = new List<Task>(); 
var sourceDirectory = "source-directory"; 
var destinationDirectory = "destination-directory"; 

foreach (var filePath in Directory.EnumerateFiles(sourceDirectory)) 
{ 
    var move = new Task(() => 
    { 
     File.Move(filePath, Path.Combine(destinationDirectory, Path.GetFileName(filePath))); 

     //UPDATE DB 
    }, TaskCreationOptions.PreferFairness); 
    move.Start(); 

    moves.Add(move); 

    if (moves.Count >= numberOfConcurrentMoves) 
    { 
     Task.WaitAll(moves.ToArray()); 
     moves.Clear(); 
    } 
} 

Task.WaitAll(moves.ToArray()); 
Смежные вопросы