Алгоритм обхода дерева для структур каталогов с большим количеством файлов

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

EDIT: В ответ на комментарий alphazero я использую PHP на машине Linux.

Ответ 1

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

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

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

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

Ответ 2

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

Трудно сказать, почему именно ваша DFS работает медленнее, даже не видя вашей реализации. Вы уверены, что не посещаете одни и те же узлы более одного раза из-за ссылок/ярлыков в вашей файловой системе? Вероятно, вы также получите значительное ускорение, если вы используете явную DFS на основе стека, а не рекурсию.

Ответ 3

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

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

Ответ 4

Структура каталога Travse с использованием BFS (как отметил Игорь).

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

И уничтожьте поток, как только он закончит листинг /travseing файлы.

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

Пример:

root

  - d1
    - d1.1
    - d1.2
    - f1.1 ... f1.100

  - d2
    - d2.1
    - d2.2
    - d2.3
    - f2.1 ... f2.200

  - d3 
    ....

OUTPUT может выглядеть так: >

 got d1

   started thread to get files of d1

   got d2

   started thread to get files of d1

   done with files in d1

   got d3

   started thread to get files of d1

   got d1.1
   started thread to get files of d1.1

   got d1.2
   started thread to get files of d1.2

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

Надеюсь, это полезно.

Ответ 5

Это будет наиболее эффективно в Windows (класс DirectoryTreeReader), сначала использует дыхание и сохраняет каждую директорию.

static const uint64 DIRECTORY_INDICATOR = -1;//std::numeric_limits <uint64>::max();

class DirectoryContent {

public:
    DirectoryContent(const CString& path)
    : mIndex(-1) 
    {
        CFileFind finder;
        finder.FindFile(path + L"\\*.*");
        BOOL keepGoing = FALSE;
        do {
            keepGoing = finder.FindNextFileW();
            if (finder.IsDots()) {
                // Do nothing...
            } else if (finder.IsDirectory()) {
                mPaths.push_back(finder.GetFilePath());
                mSizes.push_back(DIRECTORY_INDICATOR);
            } else {
                mPaths.push_back(finder.GetFilePath());
                mSizes.push_back(finder.GetLength());
            }
        } while(keepGoing);
    }

    bool OutOfRange() const {
        return mIndex >= mPaths.size();
    }
    void Advance() {
        ++mIndex;
    }
    bool IsDirectory() const {
        return mSizes[mIndex] == DIRECTORY_INDICATOR;
    }
    const CString& GetPath() const {
        return mPaths[mIndex];
    }
    uint64 GetSize() const {
        return mSizes[mIndex];
    }

private:
    CStrings mPaths;
    std::vector <uint64> mSizes;
    size_t mIndex;
};

class DirectoryTreeReader{
    DirectoryTreeReader& operator=(const DirectoryTreeReaderRealtime& other) {};
    DirectoryTreeReader(const DirectoryTreeReaderRealtime& other) {};

public:
    DirectoryTreeReader(const CString& startPath)
    : mStartPath(startPath){
        Reset();
    }

    void Reset() {
        // Argh!, no clear() in std::stack
        while(!mDirectoryContents.empty()) {
            mDirectoryContents.pop(); 
        }
        mDirectoryContents.push( DirectoryContent(mStartPath) );
        Advance();
    }
    void Advance() {
        bool keepGoing = true;
        while(keepGoing) {
            if (mDirectoryContents.empty()){
                return;
            }
            mDirectoryContents.top().Advance();
            if (mDirectoryContents.top().OutOfRange()){
                mDirectoryContents.pop();
            } else if ( mDirectoryContents.top().IsDirectory() ){
                mDirectoryContents.push( DirectoryContent(mDirectoryContents.top().GetPath()) );
            } else {
                keepGoing = false;
            }
        }
    }
    bool OutOfRange() const {
        return mDirectoryContents.empty();
    }
    const CString& GetPath() const {
        return mDirectoryContents.top().GetPath();
    }
    uint64 GetSize() const {
        return mDirectoryContents.top().GetSize();
    }

private:
    const CString mStartPath;
    std::stack <DirectoryContent> mDirectoryContents;
};