Git действительно медленный для 100 000 объектов. Какие-нибудь исправления? - PullRequest
52 голосов
/ 23 июля 2010

У меня есть «свежее» репозиторий git-svn (11,13 ГБ), в котором содержится более 100 000 объектов.

Я предварительно сформировал

git fsck
git gc

в репо после первоначальногоОформить заказ.

Затем я попытался сделать

git status

Время, необходимое для создания статуса git, составляет от 2 до 25,578 с и 2 м 53,901 с

.git status, выполнив команду

time git status

5 раз, и все время выполнялось между двумя указанными выше периодами.

Я делаю это на Mac OS X, локально, а не черезВМ.

Не может быть так долго.

Есть идеи?Помощь?

Спасибо.

Редактировать

Рядом со мной сидит коллега с сопоставимой коробкой.Меньше ОЗУ и запуск Debian с файловой системой jfs.Его git status запускается в .3 в том же репо (это также проверка git-svn).

Кроме того, я недавно изменил права доступа к файлам (на 777) в этой папке иэто значительно сократило время (я понятия не имею).Теперь я могу сделать это где-нибудь между 3 и 6 секундами.Это выполнимо, но все еще боль.

Ответы [ 12 ]

28 голосов
/ 27 июля 2010

Дело дошло до нескольких пунктов, которые я вижу прямо сейчас.

  1. git gc --aggressive
  2. Открытие файла разрешений на 777

Должно быть что-то еще, но именно это явно оказало наибольшее влияние.

17 голосов
/ 26 июля 2010

git status должен просматривать каждый файл в репозитории каждый раз.Вы можете сказать ему, чтобы он прекратил смотреть на деревья, с которыми вы не работаете

git update-index --assume-unchanged <trees to skip>

source

С man-страницы:

Когда указаны эти флаги, имена объектов, записанные для путей, не обновляются.Вместо этого эти опции устанавливают и сбрасывают бит «предположить, что он не изменен» для путей.Когда бит «предположить, что без изменений» включен, 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-неизмененного объекта, сливающегося из восходящего потока, становится конфликтом слияния.Вы можете избежать этой проблемы с помощью процесса перебазирования.

5 голосов
/ 28 апреля 2017

git status должно быть быстрее в Git 2.13 (2-й квартал 2017 г.), потому что:

По этому последнему пункту смотрите commit a33fc72 (14 апреля 2017) Джефф Хостетлер (jeffhostetler) .
(Объединено с Junio ​​C Hamano - gitster - в commit cdfe138 , 24 апреля 2017 г.)

read-cache: force_verify_index_checksum

Научите git пропускать проверку контрольной суммы SHA1-1 в конце индексного файла в verify_hdr(), который вызывается из read_index(), если не установлена ​​глобальная переменная "force_verify_index_checksum".

Научите fsck принудительно выполнить эту проверку.

Проверка контрольной суммы предназначена для выявления повреждений диска, и для небольших проектов время, необходимое для вычисления SHA-1, не так существенно, но для гигантских репозиториев этот расчет добавляет значительное время каждому сотруднику.mmand.


Git 2.14 снова улучшает производительность состояния git за счет лучшего учета «1049 * неотслеживаемого кэша », который позволяет Git пропускать чтениенеотслеживаемые каталоги, если их данные stat не изменились, используя поле mtime структуры stat.

См. Documentation/technical/index-format.txt для получения дополнительной информации о неотслеживаемом кэше.

См. коммит edf3b90 (08 мая 2017 г.) от Дэвида Тернера (dturner-tw) .
(объединено Junio ​​C Hamano -- gitster - в commit fa0624f , 30 мая 2017 г.)

Когда "git checkout", "git merge" и т. Д.манипулируя внутренним индексом, различные части информации в расширениях индекса отбрасываются из исходного состояния, поскольку обычно они не обновляются и не синхронизируются с операцией над основным индексом.

Расширение неотслеживаемого кэша теперь копируется в эти операции, что ускорит «состояние git» (при условии, что кэш должным образом признан недействительным).


В целомзапись в кеш также будет быстрее с Git 2.14.x / 2.15

См. commit ce012de , commit b50386c , commit 3921a0b (21 августа 2017 г.) Кевин Уилфорд (``) .
(объединено Junio ​​C Hamano - gitster - в commit 030faf2 , 27 августа 2017 г.)

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

[Это] позволит сэкономить где-то 3-7%, если в индексе более миллиона записей без снижения производительности при небольших репозиториях.


Обновление в декабре 2017 года: Git 2.16(Q1 2018) предложит дополнительное улучшение, на этот раз для git log, так как код для итерации по loose объектные файлы только что были оптимизированы.

См. commit 163ee5e (04.12.2017) от Derrick Stolee (derrickstolee) .
(Объединено Junio ​​C Hamano - 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 Для 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 загружать индекс быстрее.

См. commit 77ff112 , commit 3255089 , commit abb4bb8 , коммит c780b9c , коммит 3b1d9e0 , коммит 371ed0d (10 октября 2018) Бен Пирт (benpeart) .
См. коммит 252d079 (26 сентября 2018 г.) от Нгуен Тай Тай Нгуц Дуй (pclouds) .
(Объединено Junio ​​C Hamano - gitster -- in commit e27bfaa , 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 2.21 (Q1 2019) представляет новое улучшение с обновлением кэша свободных объектов , используемого для оптимизации поиска существования,который был обновлен.

См. коммит 8be88db (07 января 2019) и коммит 4cea1ce , коммит d4e19e5 , коммит 0000d65 (06 января 2019 г.) Рене Шарфе (rscharfe) .
(Объединено Junio ​​C Hamano - gitster - in commit eb8638a , 18 января 2019 г.)

object-store: использовать один oid_array на каждый подкаталог для свободного кэша

Кэш свободных объектов заполненодин подкаталог за раз по мере необходимости.
Он сохраняется в oid_array, который должен быть восстановлен после каждой операции добавления.
Так что при запросе широкогодиапазон объектов, частично заполненный массив должен быть восстановлен до 255 раз, что занимает в 100 раз больше времени, чем один раз.

Используйте один oid_array для каждого подкаталога.
Это гарантирует, что записи имеютсортируется только один раз.
Он также избегает восьми шагов двоичного поиска для каждого поиска в кэше в качестве небольшого бонуса.

Кэш используется для проверки коллизий для заполнителей журнала %h, %t и %p, и мы можем видеть, как изменения ускоряют их в репозитории с ок. 100 объектов в подкаталоге:

$ git count-objects
26733 objects, 68808 kilobytes

Test                        HEAD^             HEAD
--------------------------------------------------------------------
4205.1: log with %H         0.51(0.47+0.04)   0.51(0.49+0.02) +0.0%
4205.2: log with %h         0.84(0.82+0.02)   0.60(0.57+0.03) -28.6%
4205.3: log with %T         0.53(0.49+0.04)   0.52(0.48+0.03) -1.9%
4205.4: log with %t         0.84(0.80+0.04)   0.60(0.59+0.01) -28.6%
4205.5: log with %P         0.52(0.48+0.03)   0.51(0.50+0.01) -1.9%
4205.6: log with %p         0.85(0.78+0.06)   0.61(0.56+0.05) -28.2%
4205.7: log with %h-%h-%h   0.96(0.92+0.03)   0.69(0.64+0.04) -28.1%
5 голосов
/ 17 октября 2013

Одним из долгосрочных решений является расширение git для внутреннего кэширования состояния файловой системы.

Карстен Блис сделал это для msysgit, что значительно повышает производительность в Windows. В моих экспериментах его изменение заняло время для «состояния git» с 25 секунд до 1-2 секунд на моей машине с Win7, работающей в виртуальной машине.

Изменения Карстена: https://github.com/msysgit/git/pull/94

Обсуждение подхода кеширования: https://groups.google.com/forum/#!topic/msysgit/fL_jykUmUNE/discussion

4 голосов
/ 07 марта 2014

В общем, мой Mac в порядке с git, но если в нем много незакрепленных объектов, он становится намного медленнее.Кажется, что hfs не так хорош с большим количеством файлов в одном каталоге.

git repack -ad

Затем следует

git gc --prune=now

Создает один файл пакета и удаляет все оставшиеся незакрепленные объекты.Это может занять некоторое время.

3 голосов
/ 23 июля 2010

Вы можете попробовать передать переключатель --aggressive на git gc и посмотреть, поможет ли это:

# this will take a while ...
git gc --aggressive

Также вы можете использовать git filter-branch для удаления старых коммитов и / или файлов, если у вас естьвещи, которые вам не нужны в вашей истории (например, старые двоичные файлы).

2 голосов
/ 02 октября 2011

Несмотря на это, я недавно обнаружил большое расхождение между командой git status между моей веткой master и dev.

Короче говоря, я отследил проблему до одного файла размером 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 объектов, но кажется, что оно большоефайлы представляют большую угрозу, чем множество маленьких файлов.

1 голос
/ 23 июля 2010

Вы также можете попробовать git repack

0 голосов
/ 10 марта 2016

Попробуйте запустить команду Prune, чтобы избавиться от потерянных объектов

git remote prune origin

0 голосов
/ 24 июля 2010

Я бы создал раздел, используя другую файловую систему.HFT + всегда был для меня медленным по сравнению с аналогичными операциями в других файловых системах.

...