Git действительно медленный для 100000 объектов. Какие-нибудь исправления?
У меня есть "свежее" репозиторий git-svn (11,13 ГБ), в котором более 100000 объектов.
Я предварительно сформировал
git fsck
git gc
на репо после первоначальной проверки.
Затем я попытался сделать
git status
Время, необходимое для создания статуса git, составляет от 2 до 25,978 с и от 2 до 53,901 с.
Я проверил состояние git, выполнив команду
time git status
5 раз и все время пробегали между двумя указанными выше временами.
Я делаю это на Mac OS X, а не через виртуальную машину.
Нет никакого способа, которым это должно занять так много времени.
Есть идеи? Помогите?
Благодарю.
редактировать
Рядом со мной сидит сотрудник с сопоставимой коробкой. Меньше оперативной памяти и запуск Debian с файловой системой jfs. Его статус git работает в.3 в том же репо (это также проверка git-svn).
Кроме того, я недавно изменил права доступа к файлам (на 777) для этой папки, и это значительно сократило время (почему, я понятия не имею). Теперь я могу сделать это где-нибудь между 3 и 6 секундами. Это управляемо, но все же боль.
12 ответов
Дело дошло до пары вещей, которые я вижу прямо сейчас.
git gc --aggressive
- Открытие файла разрешений на
777
Должно быть что-то еще происходящее, но именно это явно оказало наибольшее влияние.
git status должен каждый раз просматривать каждый файл в хранилище. Вы можете сказать это, чтобы перестать смотреть на деревья, с которыми вы не работаете
git update-index --assume-unchanged <trees to skip>
Из справочной страницы:
Когда эти флаги указаны, имена объектов, записанные для путей, не обновляются. Вместо этого эти опции устанавливают и сбрасывают бит "предположить, что он не изменен" для путей. Когда бит "предположить, что без изменений" включен, git перестает проверять файлы рабочего дерева на предмет возможных изменений, поэтому вам нужно вручную сбросить бит, чтобы сообщить git, когда вы изменяете файл рабочего дерева. Это иногда полезно при работе с большим проектом в файловой системе с очень медленным системным вызовом lstat(2) (например, cifs).
Эта опция также может использоваться в качестве грубого механизма на уровне файлов, чтобы игнорировать незафиксированные изменения в отслеживаемых файлах (аналогично тому, что.gitignore делает для неотслеживаемых файлов). Git потерпит неудачу (изящно), если ему нужно изменить этот файл в индексе, например, при слиянии в коммите; таким образом, в случае, если предполагаемый неотслеживаемый файл будет изменен в восходящем направлении, вам нужно будет обработать ситуацию вручную.
Многие операции в git зависят от вашей файловой системы, чтобы иметь эффективную реализацию lstat(2), так что информацию st_mtime для файлов рабочего дерева можно дешево проверить, чтобы увидеть, изменилось ли содержимое файла по сравнению с версией, записанной в индексном файле. К сожалению, некоторые файловые системы имеют неэффективный lstat(2). Если ваша файловая система является одной из них, вы можете установить бит "предположить неизменным" для путей, которые вы не изменили, чтобы git не выполнял эту проверку. Обратите внимание, что установка этого бита в пути не означает, что git проверит содержимое файла, чтобы увидеть, изменился ли он - он заставляет git пропустить любую проверку и предположить, что он не изменился. Когда вы вносите изменения в рабочие файлы дерева, вы должны явно сообщить об этом git, удалив бит "предположить неизменным", либо до, либо после их изменения.
...
Чтобы установить бит "предположить неизменным", используйте опцию --assume-unchanged. Для сброса используйте --no-предположить-без изменений.
Команда просматривает конфигурационную переменную core.ignorestat. Когда это так, пути обновляются путями обновления git-index… и путями обновляются другими командами git, которые обновляют и индекс, и рабочее дерево (например, git apply --index, git checkout-index -u и git read-tree -u) автоматически помечаются как "предполагается, что без изменений". Обратите внимание, что бит "предположить, что нет изменений" не устанавливается, если git update-index --refresh находит, что файл рабочего дерева соответствует индексу (используйте git update-index --really-refresh, если вы хотите пометить их как "предположить, что они не изменены").
Теперь ясно, что это решение будет работать, только если есть части репо, которые вы можете легко игнорировать. Я работаю над проектом аналогичного размера, и есть определенно большие деревья, которые мне не нужно регулярно проверять. Семантика git-status делает его, как правило, проблемой O(n) (количество файлов n). Вам нужны специфичные для домена оптимизации, чтобы добиться большего.
Обратите внимание, что если вы работаете в шаблоне сшивания, то есть если вы интегрируете изменения из восходящего потока путем слияния, а не перебазирования, то это решение становится менее удобным, потому что изменение объекта --assume-неизмененного, объединяющегося из восходящего потока, становится слиянием конфликт. Вы можете избежать этой проблемы с помощью процесса перебазирования.
git status
должен быть быстрее в Git 2.13 (2 квартал 2017 года), потому что:
- оптимизация вокруг массива оптимизации строк (см. " способы улучшения"
git status
производительность ") - Лучшее управление "чтение кеша".
Об этом последнем пункте см. Commit a33fc72 (14 апреля 2017 г.) Джеффа Хостетлера ( jeffhostetler
)
(Объединено Юнио С Хамано - gitster
- в коммите cdfe138, 24 апреля 2017 г.)
read-cache
:force_verify_index_checksum
Научите git пропускать проверку контрольной суммы SHA1-1 в конце индексного файла в
verify_hdr()
который вызывается изread_index()
если "force_verify_index_checksum
"глобальная переменная установлена.Учат
fsck
заставить эту проверку.Проверка контрольной суммы предназначена для обнаружения повреждения диска, и для небольших проектов время, необходимое для вычисления SHA-1, не так существенно, но для гигантских репозиториев этот расчет добавляет значительное время каждой команде.
Git 2.14 снова улучшает производительность состояния git, лучше принимая во внимание " неотслеживаемый кеш ", который позволяет Git пропускать чтение неотслеживаемых каталогов, если их stat
данные не изменились, используя mtime
поле stat
состав.
Увидеть Documentation/technical/index-format.txt
больше на неотслеживаемом кеше.
Смотрите коммит edf3b90 (08 мая 2017 г.) Дэвида Тернера ( dturner-tw
)
(Объединено Юнио С Хамано - gitster
- в комитете fa0624f, 30 мая 2017 г.)
Когда "
git checkout
","git merge
"и т. д. манипулирует внутренним индексом, различные части информации в расширениях индекса отбрасываются из исходного состояния, так как обычно это не тот случай, когда они обновляются и синхронизируются с операцией по основной индекс.Расширение неотслеживаемого кэша теперь копируется во все эти операции, что ускорит "состояние git" (при условии, что кэш должным образом признан недействительным).
В более общем смысле, запись в кеш также будет быстрее с Git 2.14.x/2.15
Смотрите коммит ce012de, коммит b50386c, коммит 3921a0b (21 августа 2017 г.) Кевина Уилфорда (``).
(Объединено Юнио С Хамано - gitster
- в коммите 030faf2, 27 августа 2017 г.)
Мы привыкли тратить больше, чем необходимо, на выделение и освобождение части памяти при записи каждой записи индекса.
Это было оптимизировано.[Это] позволит сэкономить от 3 до 7%, если в индексе будет более миллиона записей без снижения производительности на небольших репозиториях.
Обновление в декабре 2017 года: Git 2.16 (Q1 2018) предложит дополнительное улучшение, на этот раз для git log
, поскольку код для перебора свободных объектных файлов просто оптимизирован.
Смотрите коммит 163ee5e (04 декабря 2017 г.) от Деррика Столи ( derrickstolee
)
(Объединено Юнио С Хамано - gitster
- в коммите 97e1f85, 13 декабря 2017 г.)
sha1_file
: использоватьstrbuf_add()
вместоstrbuf_addf()
Заменить использование
strbuf_addf()
сstrbuf_add()
при перечислении незакрепленных объектов вfor_each_file_in_obj_subdir()
, Поскольку мы уже проверяем длину и шестнадцатеричные значения строки перед использованием пути, мы можем предотвратить дополнительные вычисления, используя метод более низкого уровня.Один потребитель
for_each_file_in_obj_subdir()
это код аббревиатуры. Сокращения OID ( идентификаторы объектов) используют кэшированный список незакрепленных объектов (для каждого подкаталога объекта) для быстрого выполнения повторных запросов, но при большом количестве незакрепленных объектов время загрузки кеша значительно.Большинство репозиториев не имеют много незакрепленных объектов перед перепаковкой, но в случае GVFS (см. " Объявление GVFS (Git Virtual File System) ")) репозитории могут вырасти до миллионов незакрепленных объектов.
Профилирование производительности 'git log' в Git For Windows в репозитории с поддержкой GVFS с ~2,5 миллионами незакрепленных объектов показало, что 12% процессорного времени было потрачено наstrbuf_addf()
,Добавить новый тест производительности в
p4211-line-log.sh
это более чувствительно к этой загрузке кеша.
Ограничивая до 1000 коммитов, мы больше напоминаем время ожидания пользователя при чтении истории в пейджер.Для копии репозитория Linux с двумя ~512 МБ пакетными файлами и ~572 КБ незакрепленных объектов выполнение 'git log --oneline --parents --raw -1000' имело следующую производительность:
HEAD~1 HEAD
----------------------------------------
7.70(7.15+0.54) 7.44(7.09+0.29) -3.4%
Обновление марта 2018: Git 2.17 улучшится git status
еще немного: см. этот ответ.
Обновление: Git 2.20 (Q4 2018) добавляет таблицу смещения записи индекса (IEOT), которая позволяет git status
загружать индекс быстрее.
См. Коммит 77ff112, коммит 3255089, коммит abb4bb8, коммит c780b9c, коммит 3b1d9e0, коммит 371ed0d (10 октября 2018 г.) от Ben Peart ( benpeart
)
См. Коммит 252d079 (26 сентября 2018 г.) Нгуен Тхай Нгук Дуй ( pclouds
)
(Объединено Юнио С Хамано - gitster
- в комм. е27бфаа, 19 октября 2018 г.)
read-cache: загружать записи кэша в рабочие потоки
Этот патч помогает решить проблему загрузки индекса процессором с помощью таблицы смещения записей индекса (IEOT), чтобы разделить загрузку и преобразование записей кэша между несколькими потоками параллельно.
я использовал
p0002-read-cache.sh
сгенерировать некоторые данные о производительности:Test w/100,000 files reduced the time by 32.24% Test w/1,000,000 files reduced the time by -4.77%
Обратите внимание, что в случае с 1 000 000 файлов многопоточность при разборе записи в кэш не приводит к выигрышу в производительности. Это связано с тем, что затраты на анализ расширений индекса в этом репо намного превышают затраты на загрузку записей кэша.
Это позволяет:
config
: Добавить новоеindex.threads
настройка конфигурацииДобавить поддержку для нового
index.threads
параметр конфигурации, который будет использоваться для управления кодом потоков вdo_read_index()
,
- Значение 0 будет указывать код индекса для автоматического определения правильного количества потоков для использования.
Значение 1 сделает код однопоточным.- Значение больше 1 установит максимальное количество потоков для использования.
В целях тестирования этот параметр можно перезаписать, установив
GIT_TEST_INDEX_THREADS=<n>
переменная окружения со значением больше 0.
Одним из долгосрочных решений является добавление git для внутреннего кэширования состояния файловой системы.
Karsten Blees сделал это для msysgit, что значительно повышает производительность в Windows. В моих экспериментах его изменение заняло время для "состояния git" с 25 секунд до 1-2 секунд на моей машине Win7, работающей в виртуальной машине.
Изменения Карстена: https://github.com/msysgit/git/pull/94
Обсуждение подхода к кэшированию: https://groups.google.com/forum/
В общем, мой Mac в порядке с git, но если в нем много незакрепленных объектов, он становится намного медленнее. Кажется, hfs не так хорош с большим количеством файлов в одном каталоге.
git repack -ad
С последующим
git gc --prune=now
Создайте один файл пакета и удалите все оставшиеся незакрепленные объекты. Это может занять некоторое время.
Вы можете попробовать передать --aggressive
переключиться на git gc
и посмотрим, поможет ли это:
# this will take a while ...
git gc --aggressive
Также вы можете использовать git filter-branch
удалить старые коммиты и / или файлы, если у вас есть вещи, которые вам не нужны в вашей истории (например, старые двоичные файлы).
Для чего это стоит, я недавно обнаружил большое расхождение между git status
Команда между моим мастером и ветвями разработчика.
Короче говоря, я отследил проблему до одного файла размером 280 МБ в корневом каталоге проекта. Это была случайная проверка дампа базы данных, поэтому было нормально удалить его.
Вот до и после:
⚡ time git status
# On branch master
nothing to commit (working directory clean)
git status 1.35s user 0.25s system 98% cpu 1.615 total
⚡ rm savedev.sql
⚡ time git status
# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: savedev.sql
#
no changes added to commit (use "git add" and/or "git commit -a")
git status 0.07s user 0.08s system 98% cpu 0.157 total
У меня в хранилище 105 000 объектов, но кажется, что большие файлы представляют большую опасность, чем многие маленькие файлы.
Попробуйте запустить команду Prune, она избавится от незакрепленных предметов
git remote prune origin
Возможно прожектор пытается проиндексировать файлы. Возможно, отключите прожектор для вашего кода dir. Проверьте Activity Monitor и посмотрите, какие процессы запущены.
Я бы создал раздел, используя другую файловую систему. HFT+ всегда был для меня медленным по сравнению с аналогичными операциями в других файловых системах.
Может быть, вы используете сканер вирусов? Я протестировал несколько больших проектов здесь, на Windows и на Linux - это было чертовски быстро!
Я не думаю, что вам нужно делать git gc в клонированном репо (оно должно быть чистым).
Ваш жесткий диск в порядке? IOPS и R/W в секунду? Может быть, он поврежден?