Быстрый (быстрый?) Способ получить количество файлов в каталоге с более чем 200 000 файлов

У меня есть несколько каталогов, содержащих тестовые данные, обычно более 200 000 маленьких (~ 4k) файлов для каждого каталога.

Я использую следующий код С#, чтобы получить количество файлов в каталоге:

int fileCount = System.IO.Directory.GetFiles(@"C:\SomeDirectory").Length;

Это очень, очень медленно, но есть ли альтернативы, которые я могу использовать?

Изменить

Каждая папка содержит данные за один день, и у нас будет около 18 месяцев каталогов (~ 550 каталогов). Я также очень заинтересован в повышении производительности, которое люди обнаружили, переработав структуры плоских каталогов более вложенными.

Ответы

Ответ 1

Не использовать пространство имен System.IO.Directory, нет. Вам нужно будет найти способ запроса к каталогу, который не требует создания массивного списка файлов.

Это кажется немного надзором со стороны Microsoft, у API Win32 всегда были функции, которые могли бы подсчитывать файлы в каталоге.

Вы также можете рассмотреть возможность разделения вашего каталога. Как вы управляете каталогом из 200 000 файлов вне меня: -)

Update:

Джон Сондерс поднимает хороший момент в комментариях. Мы уже знаем, что файловые системы общего назначения недостаточно хорошо подходят для работы с этим уровнем хранения. Одна вещь, которая оборудована для обработки огромного количества небольших "файлов", - это база данных.

Если вы можете идентифицировать ключ для каждого (содержащий, например, дату, час и номер клиента), эти файлы должны быть введены в базу данных. Размер записи 4K и 108 миллионов строк (200 000 строк/день * 30 дней/месяц * 18 месяцев) должны быть легко обработаны большинством профессиональных баз данных. Я знаю, что DB2/z жует это на завтрак.

Затем, когда вам нужны некоторые тестовые данные, извлеченные в файлы, у вас есть программа script/, которая просто извлекает соответствующие записи в файловую систему. Затем запустите ваши тесты для успешного завершения и удаления файлов.

Это должно сделать вашу конкретную проблему довольно легко:

select count(*) from test_files where directory_name = '/SomeDirectory'

если у вас есть указатель на имя_каталога, конечно.

Ответ 2

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

Однако вы почти наверняка не найдете решений, которые работают намного быстрее, чем это.

Почему?

Контроль доступа.

Каждый файл в каталоге может иметь список управления доступом, что может помешать вам видеть файл вообще.

Сама операционная система не может просто сказать "эй, здесь есть 100 файлов", потому что некоторые из них могут представлять файлы, которые вам не позволяют знать, - они вообще не должны отображаться вам. Таким образом, сама ОС должна перебирать файлы, проверяя файл разрешений доступа по файлу.

Для более детального обсуждения этого вопроса см. два сообщения из The Old New Thing:

[В стороне, если вы хотите повысить производительность каталога, содержащего большое количество файлов, ограничьте себя строго 8.3 именами файлов. Нет, я не шучу - это быстрее, потому что ОС не нужно генерировать имя файла 8.3, и потому, что используемый алгоритм - это braindead. Попробуйте тест, и вы увидите.]

Ответ 3

FYI,.NET 4 включает новый метод Directory.EnumerateFiles, который делает именно то, что вам нужно здорово. Скорее всего, вы не используете .NET 4, но все равно стоит помнить!

Изменить: Теперь я понимаю, что OP хотел NUMBER файлов. Однако этот метод настолько полезен, что я сохраняю этот пост здесь.

Ответ 4

У меня была очень похожая проблема с каталогом, содержащим (мы думаем) ~ 300 000 файлов.

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

Мы сделали это, создав каталоги a-z, представляя первую букву файла, затем подкаталоги для каждого из них, также содержащие a-z для второй буквы файла. Затем мы вставили файлы в соответствующий каталог

например.

gbp32.dat

отправился в

g/b/gbp32.dat

и переписал наши подпрограммы доступа к файлам соответствующим образом. Это имело огромное значение, и это относительно тривиально (я думаю, мы перемещали каждый файл, используя 10-строчный Perl script)

Ответ 5

Вы можете использовать System.Management и класс WMI "cim_datafile", просто запустите следующий запрос в WMI, вы также можете использовать Linq to Wmi, но я не пробовал его

select * from cim_datafile where drive='c:' and path='\\SomeDirectory\\' 

Я думаю, что он будет работать быстрее

Ответ 6

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

Ответ 7

Если вы не боитесь вызывать функции win32, возможно, стоит попробовать FIndFirstFile, а затем повторить с FindNextFile. Это экономит накладные расходы, выделяя все эти строки, чтобы получить счет.

Ответ 8

Создайте индекс каждый день в полночь. Поиск файла будет очень быстрым. И подсчет количества файлов так же тривиален.

Если я это вижу правильно, у вас есть один dir для каждого дня. Если все файлы, которые вы получили сегодня, отправляются на карту сегодняшнего дня, эта система может быть улучшена. Просто проиндексируйте каталог предыдущего дня в полночь.

Ответ 9

Если я использую медленный язык высокого уровня, и переносимость не вызывает большого беспокойства, у меня возникнет соблазн попробовать внешнюю программу (например, `ls | wc`.first.to_i , если вы используете ruby ​​и unix), но потом я проверю, улучшает ли он работу.