Pull to refresh

Comments 25

Я использую fzf — find и grep/ag в одном флаконе с интеграцией в vim и кучей плюшек. Тоже написано на Go и работает очень быстро.

Таким образом вы лишаетесь кроссплатформенности. А как насчёт быстродействия Walk?
Walk использует Readdirnames, так что производительность особо не отличается.
На других платформах можно использовать generic-код, это ничему не противоречит
Нас всячески отговаривают использовать этот системный вызов, но ту же утилиту find это ни капельки не смущает.

Удивительно, не правда ли? Учитывая, что getdents — нынче единственный системный вызов для чтения директории
Дело в том, что find явно не просто зовет readdir, ибо «знает», какие файлы являются директориями, не делая stat
Почему? В линуксе readdir возвращает тип файла в структуре: http://linux.die.net/man/3/readdir
Чорт. Ну ок, тогда find, видимо, просто использует linux-specific поля readdir. В go в readdir ничего такого нет :(
Вернее, filemode есть и есть метод IsDir(), но это достигается вызовом Stat(). Если говорить про Readdirnames(), то там только имена.
Что-то мне подсказывает, что у find исходники есть, и там можно посмотреть на происходящее.
Но из всего описанного я делаю вывод, что стандартная библиотека go ещё не до конца оптимизирована… Либо нужна параллельная реализация, которая будет тратить память, но буферизировать данные, полученные от системы.
Надо быть аккуратным вот с этим:
http://linux-tips.org/t/readdir-function-on-xfs-filesystem-not-working-properly/115
сканирование изменений в директории

по аналогии с утилитой find


find(1) умеет сканировать изменения, или я не так понял?
Видимо речь про diff списка файлов.
Не умеет, разве что с фильтром по mtime можно получить нечто похожее. Я имел в виду, что на основе списка файлов можно определять изменения, если где-то хранить предыдущее состояние, как это делает git, например

Если речь идёт о периодическом сканировании, скажем, раз в день, то непонятно, зачем делать быстрее, чем find. Если нужно непрерывно это делать, но полное сканирование это очень плохой подход — медленный и нагружает диск и ядро. Смотрите inotify(7) и другие подобные инструменты.


Но сама тема разбора программ из coreutils однозначно интересна. Я когда-то разбирал sort(1), чтобы посмотреть, как она делает сортировку по кускам.

Предупреждаю т.к. недавно наткнулся: в любимой под Linux файловой системе XFS это работать не будет, без спец настройки ФС. :(
В итоге кусок кода на stat переводил обратно (но там время обхода не кретично).
В документации об этом написано, и в этом случае должен возвращаться DT_UNKNOWN. В полном коде примера есть кусок, который делает stat(), когда получает такое значение
Oдно ограничение вашего кода (и исходного и модифицированного), которого нет у find(1), это невозможность работать с деревом, в котором длина путей превышает MAXPATHLEN. Поэтому find делает fchdir в директорию и из нее. Ну и при DT_UNKNOWN делать stat. У find есть еще одна оптимизация: от смотрит на nlink *родительской* директории. Это число на 1 больше числа поддиректорий (из-за dotdot). Поэтому если nlink == 1, то поддиректорий гарантировано нет (это самый распространенный случай — большинство директорий листьевые) и делать stat в случае DT_UNKNOWN не нужно.
Кстати говоря, ограничения на MAXPATHLEN не должно быть, если не требуется делать Stat (т.е. на семейсте файловых систем ext, например). Я использую openat, а имя директории передаю только для того, чтобы напечатать его на экран.

В целом, я не буду спорить, что код (причём полная версия, а не та, что в статье) обрабатывает DT_UNKNOWN весьма неоптимально, и я даже перепутал Stat() с Lstat(), когда это делал.

Про значение nlink для директорий никогда не задумывался, спасибо! Впрочем, если вы не собираетесь получать DT_UNKNOWN (т.е. если ваши ФС это ext* или btrfs), то эта оптимизация не нужна :).
Кстати, ext* может быть создана без флага filetype (mkfs -O ^filetype). Этом случае всегда возвращается DT_UNKNOWN.
Есть ещё fts, и там тоже есть флаг FTS_NOSTAT, чтобы не делать stat при проходе по иерархии.
Да, убрать аллокации, возможно, помогло бы. Но если это всё аллоцируется на стеке (что надо проверить), то выигрыш по сути будет нулевой.
Если уберете рекурсию — стек будет много меньше.
Sign up to leave a comment.

Articles