Самый быстрый способ сканирования рекурсивных каталогов NTFS в C ++ - PullRequest
6 голосов
/ 15 июня 2010

Я написал небольшой сканер для сканирования и преобразования структур каталогов.

Это основано на dirent (который является маленькой оболочкой для FindNextFileA) В моих первых тестах это удивительно медленно:

около 123473 мс для 4500 файлов (локальный Samsung ThinkPad T60P, 320 ГБ, 2,5 "HD). 121481 файлов найдено за 123473 миллисекунд Это нормальная скорость?

Это мой код:

int testPrintDir(std::string  strDir, std::string strPattern="*", bool recurse=true){
  struct dirent *ent;
  DIR *dir;
  dir = opendir (strDir.c_str());
  int retVal = 0;
  if (dir != NULL) {
    while ((ent = readdir (dir)) != NULL) {
      if (strcmp(ent->d_name, ".") !=0 &&  strcmp(ent->d_name, "..") !=0){
        std::string strFullName = strDir +"\\"+std::string(ent->d_name);
        std::string strType = "N/A";
        bool isDir = (ent->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0;
        strType = (isDir)?"DIR":"FILE";                 
        if ((!isDir)){
             //printf ("%s <%s>\n", strFullName.c_str(),strType.c_str());//ent->d_name);
          retVal++;
        }   
        if (isDir && recurse){
             retVal += testPrintDir(strFullName, strPattern, recurse);
        }
      }
    }
    closedir (dir);
    return retVal;
  } else {
    /* could not open directory */
    perror ("DIR NOT FOUND!");
    return -1;
  }
}

Ответы [ 4 ]

4 голосов
/ 15 июня 2010

Есть некоторые обстоятельства, когда такая скорость нормальная, да. Во-первых, использование FindFirstFileA вместо FindFirstFileW приведет к накладным расходам при преобразовании из UTF-16 в ANSI. Во-вторых, если вы просматриваете каталоги, к которым операционная система еще не обращалась, вы будете подвергаться, по крайней мере, одному штрафу за поиск (около 16 мс для большинства потребительских жестких дисков), ограничивая количество перечислений до 100 проверок каталогов в секунду. Это ухудшится, если таблица основных файлов на данном диске будет сильно фрагментирована.

Что касается количества файлов, то оно будет зависеть больше от количества файлов в каталоге, чем от количества самих файлов.

3 голосов
/ 15 июня 2010

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

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

РЕДАКТИРОВАТЬ: уточнение, как вы должны посещать каталоги.Технически это не первый поиск в ширину.

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

Возможно, диск является узким местом. Но вы можете попробовать:

  1. Операции со строками можно оптимизировать - используйте массивы символов, например, из std :: string.
  2. Построение strFullName при каждом рекурсивном вызове не требуется. Используйте один фиксированный буфер символов (то есть статический массив внутри функции), измените его немедленно.
  3. Не передавайте strPattern по значению!
  4. Не создавайте strType до отладки
  5. Другие предложили составить список каталогов для обработки, прежде чем углубляться в рекурсию. Для его создания я предлагаю один статический массив (аналогично 2.) или использовать стек (alloca).
  6. Файловая система использует Unicode для хранения имен файлов? Если это так, то использование строк Юникода с FindFirstFileW и FindNextFileW может быть немного быстрее.
0 голосов
/ 15 июня 2010

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

...