Delphi Поиск файлов и каталогов самый быстрый алгоритм - PullRequest
5 голосов
/ 16 июня 2010

Я использую Delphi7, и мне нужно решение большой проблемы. Может ли кто-нибудь предоставить мне более быстрый способ поиска по файлам и папкам, чем использование findnext и findfirst?потому что я также обрабатываю данные для каждого файла / папки (дата создания / автор / размер / и т. д.), и это занимает много времени ... Я много искал под WinApi, но, вероятно, я не вижу лучшей функции вЧтобы выполнить это.Все примеры, которые я нашел в Delphi, используют findfirst и findnext ...

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

Спасибозаранее!

Ответы [ 10 ]

7 голосов
/ 16 июня 2010

Я думаю, что любой компонент, который вы купили бы, также использовал findfirst / findnext. Конечно, рекурсивно. Я не думаю, что есть способ посмотреть на каждый каталог и файл, фактически не просматривая каждый каталог и файл.

В качестве ориентира, чтобы увидеть, достаточно ли быстрый код ваш, сравните производительность с WinDirStat http://windirstat.info/ (как раз до того момента, когда он собрал данные и готов построить свой график использования пространства.)
Исходный код доступен, если вы хотите увидеть, что они делают. Это C, но я ожидаю, что он использует те же вызовы API.

6 голосов
/ 17 июня 2010

Единственное, что вы можете сделать, чтобы реально повысить свою производительность, - это непосредственно проанализировать MFT, если ваши тома являются NTFS. Делая это, вы можете перечислять файлы очень, очень быстро - мы говорим, по крайней мере, на порядок быстрее. Если все метаданные, которые вам нужны, являются частью записи MFT, ваши поиски будут выполняться намного быстрее. Даже если вам потребуется больше операций чтения для дополнительных метаданных, вы сможете очень быстро составить список файлов-кандидатов.

Недостатком является то, что вам придется анализировать MFT самостоятельно: для этого нет функций WinAPI, о которых я знаю. Вы также можете беспокоиться о вещах, которые оболочка обычно делает для вас, о таких вещах, как жесткие ссылки, соединения, точки повторной обработки, символические ссылки, ссылки оболочки и т. Д.

Однако, если вам нужна скорость, увеличение сложности - единственный способ ее достичь.

Мне неизвестен какой-либо доступный код Delphi, в котором уже реализован синтаксический анализатор MFT, поэтому вам, вероятно, придется либо использовать стороннюю библиотеку, либо реализовать ее самостоятельно. Я собирался предложить Open Source (GPL) NTFS Undelete , который был написан на Delphi, но он реализует синтаксический анализ MFT через код Python и имеет встроенный мост Delphi-Python.

2 голосов
/ 16 июня 2010

Если вы хотите получить действительно быстрые результаты поиска, подумайте об использовании Windows Search (API) или службы индексирования.

Другие улучшения могут заключаться в использовании потоков и разделении поиска файлов и сборе свойств файла или просто в потоке поиска.

1 голос
/ 17 июня 2010

Вы также можете попробовать BFS против DFS.Это может повлиять на вашу производительность.

http://blogs.msdn.com/b/ericlippert/archive/2004/09/27/234826.aspx

http://en.wikipedia.org/wiki/Breadth-first_search

http://en.wikipedia.org/wiki/Depth-first_search

1 голос
/ 17 июня 2010

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

TMyScanThread

Сканирование структуры файла, для каждого «попадания» добавляйте путь + файл в TList / TStringList или аналогичный с помощью Syncronize (). Не забудьте Sleep () внутри цикла, чтобы у ОС тоже было время.

Псевдокод для потока:

TMyScanThread=class(TThread)
private
  fCount : Cardinal;
  fLastFile : String;
  procedure GetListCount;
  procedure AddToList;  
public
  FileList : TStringList;
  procedure Execute; Override;
end;

procedure TMyScanThread.GetListCount;
begin
  fCount := FileList.Count;
end;

procedure TMyScanThread.AddToList;
begin
  FileList.Add(fLastFile);
end;

procedure TMyScanThread.Execute;
begin
     try
        { Get the list size }
        Syncronize( GetListCount );
        if fCount<500 then
        begin
          // FindFirst code goes here
          { Add a file to the list }
          fLastFile := SR.Name; // Store Filename in local var
          Syncronize( AddToList ); // Call method to add to list
          SleepEx(0,True);
        end else
          SleepEx(1000,True);
     finally
        Terminate;
     end;
end;

TMyProcessFilesThread

Получить самую старую запись в списке и обработать ее. Затем выведите результаты в БД.

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

Одной альтернативой вызовам Syncronize () является использование TCriticalSection. Реализация синхронизации между потоками часто является делом вкуса и задачей под рукой ...

1 голос
/ 17 июня 2010

С циклом findfirst / findnext не так много места для оптимизации, потому что он в основном связан с вводом / выводом: операционной системе необходимо прочитать эту информацию с вашего жесткого диска!

Доказательство: создайте небольшую программу, которая реализует простой цикл findfirst / findnext, который НИЧЕГО не работает с найденными файлами. Перезагрузите компьютер и запустите его в большом каталоге, обратите внимание на время, необходимое для завершения. Затем запустите его снова, не перезагружая компьютер. Вы заметите, что второй запуск значительно быстрее, потому что операционная система кэшировала информацию!

Если вы точно знаете, что каталог, который вы пытаетесь сканировать, активно используется ОС из-за какого-то другого приложения, использующего данные (это поместит информацию о структуре каталога в кэш ОС и сделает сканирование не связанным с I / O) вы можете попробовать запустить несколько циклов findfirst / findnext параллельно, используя потоки. Недостатком этого является то, что, если структура каталогов еще не полностью находится в кэше ОС, ваш алгоритм снова привязан к входу / выходу жесткого диска, и он может оказаться хуже исходного, поскольку вы сейчас делаете несколько параллельных запросов ввода-вывода, которые требуют быть обработанным тем же устройством.

Когда мне пришлось заняться этой же проблемой, я решил отказаться от параллельных циклов, потому что ВТОРОЙ запуск приложения всегда намного быстрее, доказывая, что я привязан к вводу / выводу, и никакая оптимизация ЦП не исправит ввод / вывод. узкое место.

1 голос
/ 17 июня 2010

Если ваша программа работает в Windows 7 или Server 2008 R2, в Windows FindFirstFileEx есть некоторые улучшения, которые позволят ей работать немного быстрее.Вам нужно будет скопировать и изменить функции VCL, чтобы включить новые опции.

1 голос
/ 17 июня 2010

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

Нашим решением было использование файла очереди в отдельном каталоге. Поскольку файлы «добавляются» в систему, они записываются в файл очереди (это был файл с фиксированной записью). Когда системе необходимо обработать данные, она увидит, существует ли файл, и если да, то переименует его и откроет переименованную версию (таким образом, добавления могут произойти для следующего прохода процесса). Затем файл был обработан по порядку. Затем мы заархивировали файл очереди и обработанные файлы в подкаталог на основе даты и времени (например, G: \ PROCESSED \ 2010 \ 06 \ 25 \ 1400 содержал файлы, запущенные в 14:00 25.06.2010) .

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

1 голос
/ 17 июня 2010

Если вам нужно сканировать удаленный диск с таким большим количеством файлов, я настоятельно рекомендую сделать это с помощью схемы «клиент-сервер», чтобы фактическое сканирование файлов всегда выполнялось локально, а удаленные результаты выбирались только удаленно. Это сэкономит вам много времени. Также все «серверы» могут сканировать параллельно.

0 голосов
/ 16 июня 2010

Когда у меня начались проблемы с производительностью при работе с большим количеством мелких файлов в файловой системе, я перешел к хранению файлов в виде больших двоичных объектов в базе данных.Нет причин, по которым связанная с ними информация, такая как размер, создание и автор, также не может быть сохранена в базе данных.После того, как таблицы заполнены в базе данных, я подозреваю, что механизм базы данных мог бы гораздо быстрее находить записи (файлы), чем любое решение, которое мы собираемся найти, поскольку код базы данных очень специализирован для эффективного поиска больших данныхнаборы.Это, безусловно, будет более гибким, поскольку добавление нового поиска будет так же просто, как создание нового оператора Select.Пример: выберите * из файлов, где author = 'bob' и размер> 10000

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...