Как улучшить производительность журнала git?

Я пытаюсь извлечь журналы git из нескольких таких репозиториев:

git log --pretty=format:%H\t%ae\t%an\t%at\t%s --numstat

Для больших репозиториев (например, rails / rails) создание журнала занимает более 35 секунд.

Есть ли способ улучшить эту производительность?


person George L    schedule 03.02.2016    source источник
comment
@msw для этого проекта, к сожалению, да.   -  person msw    schedule 03.02.2016
comment
Git 2.18 (второй квартал 2018 г.) должен улучшить _1_ производительность на много. См. мой ответ ниже.   -  person George L    schedule 03.02.2016
comment
Кеширование не считал, это прекрасно!   -  person VonC    schedule 10.05.2018


Ответы (4)


Вы правы, на создание отчета о 56000 коммитов, генерирующих 224000 строк (15 МБ) вывода, требуется от 20 до 35 секунд. Я действительно думаю, что это довольно приличная производительность, но вы этого не делаете; Ладно.

Поскольку вы создаете отчет с использованием постоянного формата из неизменной базы данных, вам нужно сделать это только один раз. После этого вы можете использовать кешированный результат git log и пропустить трудоемкую генерацию. Например:

git log --pretty=format:%H\t%ae\t%an\t%at\t%s --numstat > log-pretty.txt

Вы можете задаться вопросом, сколько времени уходит на поиск интересующих данных во всем отчете. Это достойный вопрос:

$ tail -1 log-pretty.txt
30  0   railties/test/webrick_dispatcher_test.rb
$ time grep railties/test/webrick_dispatcher_test.rb log-pretty.txt 
…
30  0   railties/test/webrick_dispatcher_test.rb

real    0m0.012s
…

Неплохо, введение «кеша» сократило необходимое время с 35+ секунд до десятка миллисекунд. Это почти в 3000 раз быстрее.

person msw    schedule 03.02.2016
comment
Если вы используете git версии 2.29 или выше, вам лучше запустить _1_. При этом будут предварительно вычислены пути к файлам, так что _2_ команды, привязанные к файлам, также выиграют от этого кеша. - person George L; 04.02.2016

TL; DR; как упоминается в GitMerge 2019:

git config --global core.commitGraph true
git config --global gc.writeCommitGraph true
cd /path/to/repo
git commit-graph write

На самом деле (см. В конце) первые две конфигурации не нужны с Git 2.24+ (3 квартал 2019 г.): по умолчанию они true.

Как упоминается в T4cC0re в комментарии:

Если вы используете git версии 2.29 или выше, вам лучше запустить:

git commit-graph write --reachable --changed-paths

Это предварительно вычислит пути к файлам, так что git log команды, привязанные к файлам, также выиграют от этого кеша.


Git 2.18 (второй квартал 2018 г.) улучшит git log производительность:

См. commit 902f5a2 (24 марта 2018 г.) от Рене Шарфе (rscharfe).
См. совершить 0aaf05b, зафиксировать 3d475f 2018) от Деррика Столи (derrickstolee).
См. commit 626fd98 (22 марта 2018 г.) от Брайан М. carlson (bk2204).
(Объединено Junio ​​C Hamano - gitster - < / a> в commit 51f813c, 10 апреля 2018 г.)

sha1_name: используйте bsearch_pack() для сокращений

При вычислении длины аббревиатуры для идентификатора объекта по одному пакетному файлу метод find_abbrev_len_for_pack() в настоящее время реализует двоичный поиск.
Это одна из нескольких реализаций.
Одна проблема с этой реализацией заключается в том, что он игнорирует таблицу разветвления в pack-index.

Преобразуйте этот двоичный поиск, чтобы использовать существующий bsearch_pack() метод, который правильно использует таблицу разветвления.

Благодаря использованию таблицы разветвления вычисление сокращений происходит немного быстрее, чем раньше.

Для полностью переупакованной копии репозитория Linux улучшены следующие команды git log:

* git log --oneline --parents --raw
  Before: 59.2s
  After:  56.9s
  Rel %:  -3.8%

* git log --oneline --parents
  Before: 6.48s
  After:  5.91s
  Rel %: -8.9%

Тот же Git 2.18 добавляет график фиксации: предварительное вычисление и сохранение информации, необходимой для обхода предков, в отдельном файле для оптимизации обхода графа.

См. совершить 7547b95, коммит 3d5df01, 049d51a, совершить 177722b, совершить 4f2542b, совершить 1b70dfd, совершить 2a2e32b (10 апреля 2018 г.) и зафиксировать f237c8b, rel =" noreferrer " , совершить 4ce58ee, совершить ae30d7b, b84f767, зафиксировать cfe8321, совершить f2af9f5 (2 апреля 2018 г.) от Деррик Столи (derrickstolee).
(Объединено пользователем Junio ​​C Hamano - gitster - в commit b10edb2, 8 мая 2018 г.)

commit: интегрировать график фиксации с анализом фиксации

Научите Git проверять файл графика фиксации для предоставления содержимого фиксации структуры при вызове _ 19_.
Эта реализация удовлетворяет всем пост-условиям фиксации структуры, включая загрузку родителей, корневое дерево и дату фиксации.

Если core.commitGraph равно false, не проверяйте файлы графиков.

В тестовом скрипте t5318-commit-graph .sh, добавьте output-matching условия для операций с графами только для чтения.

Загружая коммиты из графика вместо анализа буферов фиксации, мы экономим много времени на длительных обходах фиксации.

Вот некоторые результаты производительности для копии репозитория Linux, где «master» имеет 678 653 доступных коммитов и отстает от «origin/master» на 59 929 коммитов.

| Command                          | Before | After  | Rel % |
|----------------------------------|--------|--------|-------|
| log --oneline --topo-order -1000 |  8.31s |  0.94s | -88%  |
| branch -vv                       |  1.02s |  0.14s | -86%  |
| rev-list --all                   |  5.89s |  1.07s | -81%  |
| rev-list --all --objects         | 66.15s | 58.45s | -11%  |

Чтобы узнать больше о графике фиксации, см. Как работает 'git log --graph'?.


Тот же Git 2.18 (второй квартал 2018 г.) добавляет дерево отложенной загрузки.

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

См. фиксацию 279ffad (30 апреля 2018 г.) по СЕДЕР Габор (szeder).
См. зафиксировать 7b8a21d, зафиксировать 2e27bd7, , commit 891435d (6 апреля 2018 г.) Деррик Столи (derrickstolee) .
(Объединено Junio ​​C Hamano - gitster - i n совершить c89b6e1, 23 мая 2018 г.)

commit-graph: деревья с отложенной загрузкой для коммитов

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

Отложите загрузку объекта дерева для фиксации, загруженной из графа, до тех пор, пока она не будет запрошена через get_commit_tree().
Не выполняйте отложенную загрузку деревьев для коммитов не в графе, поскольку для этого требуется дублирующий синтаксический анализ и относительное улучшение производительности, когда деревья не нужны. небольшой.

В репозитории Linux тесты производительности выполнялись для следующей команды:

git log --graph --oneline -1000

Before: 0.92s
After:  0.66s
Rel %: -28.3%

Git 2.21 (первый квартал 2019 г.) добавляет свободный кеш.

См. фиксацию 8be88db (7 января 2019 г.) и совершить 4cea1ce, совершить d4e19e5, совершить 0000d65 (06 января 2019 г.) ) от Рене Шарфе (rscharfe).
(объединено с помощью Junio ​​C Hamano - gitster - в commit eb8638a, 18 января 2019 г.)

object-store: используйте по одному oid_array на подкаталог для свободного кеширования

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

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

Кэш используется для проверки коллизий для заполнителей журнала %h, %t и %p, и мы можем увидеть, как изменение ускоряет их в репозитории с ca. 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%

Git 2.22 (апрель 2019 г.) проверяет ошибки перед использованием данных, считанных из файла графика фиксации.

См. commit 93b4405, совершить 43d3561, зафиксировать 945944 >, зафиксировать f6761fa (21 февраля 2019 г.), var Arnfjör Bjarmason (_42 _).
(Объединено Junio ​​C Hamano - gitster - в commit a5e4be2, 25 апреля 2019 г.)

commit-graph напишите: не умирай, если существующий граф поврежден

Когда написано commit-graph, мы вызываем parse_commit(). Это, в свою очередь, вызовет код, который будет консультироваться с существующим commit-graph о фиксации, если граф поврежден, мы умрем.

Таким образом, мы попадаем в состояние, когда за ошибкой commit-graph verify не может следовать commit-graph write, если установлено core.commitGraph=true, для продолжения необходимо либо вручную удалить график, либо core.commitGraph нужно установить значение false.

Чтобы избежать этого, измените commit-graph write кодовый путь, чтобы использовать новый parse_commit_no_graph() помощник вместо parse_commit().
Последний вызовет repo_parse_commit_internal() с use_commit_graph=1, как показано в 177722b (commit: интегрировать график фиксации с анализом фиксации, 2018-04-10, Git v2.18.0-rc0).

Отказ от использования старого графика на некоторое время замедляет запись нового графика, но это разумный способ предотвратить распространение ошибки в существующем графике фиксации.


В Git 2.24+ (3 квартал 2019 г.) график фиксации активен по умолчанию:

См. совершить aaf633c, фиксация c6cc4c5, Деррик Столи (derrickstolee).
(объединено Junio ​​C Haman o - gitster - в commit f4f8dfe, 09 сентября 2019 г.)

commit-graph: включить график фиксации по умолчанию

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

Измените значения по умолчанию для core.commitGraph и gc.writeCommitGraph на true, чтобы пользователи могли пользоваться этой функцией по умолчанию.


По-прежнему в Git 2.24 (4 квартал 2019 г.) переменная конфигурации сообщает git fetch о необходимости записи графа фиксации после завершения.

См. фиксацию 50f26bd (3 сентября 2019 г.) по Деррик Столи (derrickstolee).
(Объединено Junio ​​C Hamano - gitster - в фиксации 5a53509, 30 сен 2019)

fetch: добавить параметр конфигурации fetch.writeCommitGraph

Функция графа фиксации теперь включена по умолчанию и по умолчанию записывается во время 'git gc'.
Обычно Git записывает граф фиксации только тогда, когда команда 'git gc --auto' передает параметр gc.auto, чтобы он действительно работал. Это означает, что график коммитов обычно отстает от тех, которые используются каждый день.

Чтобы оставаться в курсе последних коммитов, добавьте шаг в 'git fetch' для написания графика фиксации после получения новых объектов.
Параметр fetch.writeCommitGraph config позволяет писать разделенный граф фиксации, поэтому в среднем стоимость записи этого файла очень мала. Иногда цепочка фиксации-графа сворачивается до одного уровня, и это может быть медленным для очень больших репозиториев.

Для дополнительного использования измените значение по умолчанию, чтобы оно было истинным, когда feature.experimental включен.


И все же с Git 2.24 (4 квартал 2019 г.) commit-graph стал более надежным.

См. commit 6abada1, commit fbab552 (12 сентября 2019 г.), автор Джефф Кинг (peff).
(Объединено Junio ​​C Hamano - gitster - в commit 098e8c6, 7 октября 2019 г.)

commit-graph: увеличить DIE_ON_LOAD проверку до фактического времени загрузки

Фиксация 43d3561 (запись коммит-графа: не умирай, если существующий граф поврежден , 2019-03-25, Git v2.22.0-rc0) добавил переменную среды, которую мы используем только в тестовом наборе, $GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD.
Но он поставил проверку этой переменной в самом верху prepare_commit_graph(), который называется каждый раз, когда мы хотим использовать график фиксации.
Что наиболее важно, он появляется до, когда мы проверяем, какой быстрый путь мы уже пытались загрузить?, что означает, что мы в конечном итоге вызываем getenv() для каждого отдельного использование графика фиксации, а не только при загрузке.

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

Но это неэффективно. getenv() обычно должен выполнять линейный поиск в пространстве окружения.

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

[before]
Benchmark #1: git -C linux rev-list HEAD >/dev/null
Time (mean ± σ):      1.460 s ±  0.017 s    [User: 1.174 s, System: 0.285 s]
Range (min … max):    1.440 s …  1.491 s    10 runs

[after]
Benchmark #1: git -C linux rev-list HEAD >/dev/null
Time (mean ± σ):      1.391 s ±  0.005 s    [User: 1.118 s, System: 0.273 s]
Range (min … max):    1.385 s …  1.399 s    10 runs

Git 2.24 (4 квартал 2019 г.) также включает исправление регрессии.

См. фиксацию cb99a34, совершить e88aab9 (24 октября 2019 г.), (derrickstolee).
(Объединено Junio ​​C Hamano - gitster - в commit dac1d83, 4 ноября 2019 г.)

commit-graph: исправление записи первого графика фиксации во время выборки

Докладчик: Йоханнес Шинделин
Помощник: Джефф Кинг
Помощник: Седер Габор
Подписано: Деррик Столи

Предыдущая фиксация включает в себя неудачный тест на проблему с fetch.writeCommitGraph и выборку в репо с подмодулем. Здесь мы исправляем эту ошибку и устанавливаем для теста значение "test_expect_success".

Проблема возникает с этим набором команд, когда удаленное репо на <url> имеет подмодуль.
Обратите внимание, что --recurse-submodules не требуется для демонстрации ошибки.

$ git clone <url> test
$ cd test
$ git -c fetch.writeCommitGraph=true fetch origin
  Computing commit graph generation numbers: 100% (12/12), done.
  BUG: commit-graph.c:886: missing parent <hash1> for commit <hash2>
  Aborted (core dumped)

В качестве первоначального исправления я преобразовал код в builtin/fetch.c, который вызывает write_commit_graph_reachable() для запуска процесса git commit-graph записи --reachable --split. Этот код работал, но мы не хотим, чтобы эта функция работала в долгосрочной перспективе.

Этот тест продемонстрировал, что проблема должна быть связана с внутренним состоянием процесса git fetch.

Метод write_commit_graph() в commit-graph.c гарантирует, что коммиты, которые мы планируем написать закрываются из-за достижимости с помощью close_reachable().
Этот метод идет от входных коммитов и использует флаг UNINTERESTING, чтобы отметить, какие коммиты уже были посещены. Это позволяет обходу занять O(N) время, где N - это количество коммитов, а не O(P) время, где P - количество путей. (Количество путей может быть экспоненциальным числом коммитов.)

Однако флаг UNINTERESTING используется во многих местах кодовой базы. Этот флаг обычно означает некоторый барьер для остановки обхода фиксации, например, при обходе ревизий для сравнения историй.
Он не часто сбрасывается после завершения обхода, потому что начальные точки этих обходов не имеют флага UNINTERESTING, и clear_commit_marks() немедленно остановится.

Это происходит во время вызова «git fetch» с пульта дистанционного управления. Согласование выборки сравнивает удаленные ссылки с локальными и помечает некоторые коммиты как UNINTERESTING.

Я тестировал запуск clear_commit_marks_many(), чтобы очистить флаг НЕПРЕРЫВИМОСТИ внутри close_reachable(), но подсказки не имели флага, так что это ничего не дало.

Оказывается, виноват метод calculate_changed_submodule_paths(). Спасибо, Пеф, за указание на эту деталь! В частности, для каждого подмодуля collect_changed_submodules() выполняет обход ревизий, чтобы, по сути, вести историю файлов в списке подмодулей. Эти метки обхода исправлений фиксируются UNININTERESTING, если они упрощены без изменения подмодуля.

Вместо этого я наконец пришел к выводу, что мне следует использовать флаг, который не используется ни в какой другой части кода. В commit-reach.c было определено несколько флагов для обхода фиксации. алгоритмы. Флаг REACHABLE казался наиболее целесообразным, и, похоже, он фактически не использовался в файле.
Флаг REACHABLE использовался в ранних версиях _ 115_, но был удален пользователем 4fbcca4 (commit-reach: make _117 _... linear, 2018-07-20, v2.20.0-rc0).

Добавьте флаг REACHABLE в commit-graph.c и используйте его вместо UNINTEREST в close_reachable().
Это исправляет ошибку при ручном тестировании.


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

Это было исправлено в Git 2.25 (первый квартал 2020 г.).

См. совершить 7d8e72b, commit c14e6e7 (3 ноября 2019 г.), (dscho).
(Объединено Junio ​​C Hamano - gitster - в commit bcb06e2, 01 декабря 2019 г.)

fetch: добавьте параметр командной строки --write-commit-graph

Подписано: Йоханнес Шинделин

Эта опция переопределяет настройку конфигурации fetch.writeCommitGraph, если обе установлены.

А также:

fetch: избегайте проблем с блокировкой между fetch.jobs/fetch.writeCommitGraph

Подписано: Йоханнес Шинделин

Когда установлены и fetch.jobs, и fetch.writeCommitGraph, в настоящее время мы пытаемся написать график фиксации в каждом из параллельных заданий выборки, что часто приводит к сообщениям об ошибках, подобным этому:

fatal: Unable to create '.../.git/objects/info/commit-graphs/commit-graph-chain.lock': File exists.

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


Код для записи файла (ов) разбитого графа фиксации при получении вычисленного фиктивного значения для параметра, используемого при разбиении результирующих файлов, который был исправлен с помощью Git 2.25 (первый квартал 2020 г.).

См. фиксацию 63020f1 (2 января 2020 г.) по Деррик Столи (derrickstolee).
(Объединено пользователем Junio ​​C Hamano - gitster - в фиксации 037f067, 06 янв 2020 г.)

commit-graph: предпочитать значение по умолчанию size_mult при нулевом значении

Подписано: Деррик Столи

В 50f26bd (fetch: добавить параметр конфигурации fetch.writeCommitGraph, 2019-09-02 Git v2.24.0-rc0 - слияние, перечисленное в batch # 4), встроенная функция fetch добавила возможность писать график фиксации с использованием функции --split.
Эта функция создает несколько файлов графика фиксации, и они могут объединяться на основе набора параметров разделения, включая кратный размер.
По умолчанию кратный размер равен 2, что обеспечивает глубину log_2 N графа фиксации цепочка, где N - количество коммитов.

Однако во время догфудинга я заметил, что мои цепочки графиков фиксации становились довольно большими, когда оставались только сборки с помощью 'git fetch'.
Оказывается, в split_graph_merge_strategy() для переменной size_mult по умолчанию используется значение 2, за исключением того, что мы переопределяем ее с помощью split_opts контекста, если они существуют.
В _141 _, мы создаем такой split_opts,, но не заполняем его значениями.

Эта проблема возникает из-за двух сбоев:

  1. Непонятно, можно ли добавить флаг COMMIT_GRAPH_WRITE_SPLIT с NULL split_opts.
  2. Если у нас есть split_opts,, отличное от NULL, тогда мы отменяем значения по умолчанию, даже если задано нулевое значение.

Исправьте обе эти проблемы.

  • Во-первых, не переопределяйте size_mult, когда параметры предоставляют нулевое значение.
  • Во-вторых, прекратите создавать split_opts во встроенной функции выборки.

Обратите внимание, что git log был нарушен между Git 2.22 (май 2019 г.) и Git 2.27 (2 квартал 2020 г.) при использовании magic pathspec.

Синтаксический анализ командной строки git log :/a/b/ был нарушен около года, и никто не заметил, что был исправлен.

См. фиксацию 0220461 (10 апреля 2020 г.) по Джефф Кинг (peff).
См. совершить 5ff4b92 (10 апреля 2020 г.) от Junio ​​C Hamano (gitster) .
(Объединено Junio ​​C Hamano - gitster - в commit 95ca489, 22 апреля 2020 г.)

sha1-name: не предполагайте, что хранилище ссылок инициализировано

Автор: Эрико Ролим

c931ba4e (sha1-name.c``: удалить the_repo из handle_one_ref(), 2019 -16, Git v2.22.0-rc0 - слияние, указанное в пакет № 8) заменил использование помощника for_each_ref(), который работает с основным хранилищем ссылок репозитория по умолчанию. Например, с refs_for_each_ref(), который может работать с любым экземпляром хранилища ссылок, предполагая, что у экземпляра репозитория, переданного функции, хранилище ссылок уже инициализировано.

Но возможно, что его никто не инициализировал, и в этом случае код завершает разыменование указателя NULL.

А также:

repository: отметьте указатель ссылок как частный

Подписано: Джефф Кинг

Указатель refs в репозитории структуры начинает свою жизнь как NULL, но затем лениво инициализируется, когда к нему обращаются через get_main_ref_store().
Тем не менее, вызывающему коду легко забыть об этом и получить к нему прямой доступ, что приводит к работающему коду иногда, но терпит неудачу, если вызывается до того, как кто-либо другой обратится к ссылкам.

Это было причиной ошибки, исправленной 5ff4b920eb (sha1-name: не предполагайте, что ref store инициализирован, 2020-04-09, Git v2.27.0 - объединить перечислены в пакете № 3). Чтобы избежать подобных ошибок, давайте более четко обозначим поле refs как частное.

person VonC    schedule 14.04.2018
comment
@ T4cC0re Согласен. я упомянул достижимость в stackoverflow.com/a/38788417/6309. Я включил ваш комментарий в ответ для большей наглядности. - person T4cC0re; 06.06.2021
comment
См. фиксацию caf388c (09 апреля 2020 г.) и совершить e369698 (30 марта 2020 г.) от Деррик Столи (_2_).
См. - person VonC; 06.06.2021

Моей первой мыслью было улучшить ваш ввод-вывод, но я протестировал репозиторий rails с использованием SSD и получил аналогичный результат: 30 секунд.

--numstat - это то, что все замедляет, иначе git-log может завершиться за 1 секунду даже с форматированием. Выполнение различий обходится дорого, поэтому, если вы можете удалить это из своего процесса, это значительно ускорит процесс. Возможно, сделаем это постфактум.

В противном случае, если вы отфильтруете записи журнала, используя собственные средства поиска git-log, это уменьшит количество записей, которые необходимо выполнить для сравнения. Например, git log --grep=foo --numstat занимает всего одну секунду. Они находятся в документации в разделе «Ограничение фиксации». Это может значительно уменьшить количество записей, которые git должен форматировать. Диапазоны редакций, фильтры дат, фильтры авторов, поиск сообщений журнала ... все это может улучшить производительность git-log в большом репозитории, выполняя при этом дорогостоящую операцию.

person Schwern    schedule 03.02.2016

Есть еще один способ повысить git log производительность, и он основан на графиках фиксации, упомянутых в предыдущем ответе.

Git 2.27 (второй квартал 2020 г.) представляет расширение для графика фиксации, чтобы повысить эффективность проверки путей, которые были изменены при каждой фиксации, используя Фильтры Блума.

Помощник: Деррик Столи ‹[email protected]
Помощник: СЕДЕР Габор
Помощник: Джонатан Тан < / sup>
Подписано: Гарима Сингх

commit-graph: избегайте утечек памяти

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

Мы загружаем фильтры Блума на этапе prepare_revision_walk, в настоящее время только при работе с одним указателем пути.
Расширение его для работы с несколькими указателями пути может быть исследовано и построено на основе этой серии в будущем.

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

Другой ответ фильтра Блума - ": возможно", и в этом случае мы возвращаемся к полному вычислению различий, чтобы определить, был ли изменен путь в фиксации.

Мы не пытаемся использовать фильтры Блума, когда указан параметр «--walk-reflogs».
Параметр «--walk-reflogs» не проходит по цепочке предков фиксации, как остальные параметры.
Учет увеличения производительности при ходьбе записи reflog добавят больше сложности, и их можно будет изучить в следующих сериях.

Повышение производительности. Мы протестировали производительность git log -- <path> в репозитории git, linux и некоторых внутренних больших репозиториях с различными путями разной глубины.

В репозиториях git и linux:

В большом внутреннем репо с файлами, размещенными на 6-10 уровнях в дереве:

  • мы наблюдали увеличение скорости в 10–20 раз, при этом на некоторых маршрутах скорость увеличивалась до 28 раз.

Но: Исправьте (с Git 2.27, Q2 2020) утечку, замеченную фаззером.

  • У коммитов слияния есть несколько родителей, но если мы ДРЕВНИМ по отношению к нашему первому родителю в каждом диапазоне, то перекладываем ответственность за все диапазоны на первого родителя.

См. фиксацию fbda77c (4 мая 2020 г.) по Джонатан Тан (jhowtan).
(Объединено с помощью Джунио С. Хамано - gitster - в совершить 95875e a>, 8 мая 2020 г.)

Подписал: Джонатан Тан
Автор: Деррик Столи

bloom: удаление дубликатов записей каталога

Фаззер, работающий в точке входа, предоставленной fuzz-commit-graph.c обнаружил утечку памяти, когда parse_commit_graph() создает структуру bloom_filter_settings, а затем возвращается раньше срока из-за ошибки.

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

Внося это изменение, я также заметил еще одну возможную утечку памяти - когда предоставляется блок BLOOMDATA, но не BLOOMINDEXES.
Также исправьте эту ошибку.

Git 2.27 (второй квартал 2020 г.) снова улучшает фильтр цветения:


См. фиксацию b928e48 (11 мая 2020 г.) по СЕДЕР Габор (szeder).
См. зафиксировать 2f6775f, зафиксировать 65c1a28, совершить 8809328, совершить 891c17c (11 мая 2020 г.) и совершить 54c337b, совершить eb591e4 (1 мая 2020 г.), Деррик Столи (_21 _).
(Объединено Junio ​​C Hamano - gitster - в commit 4b1e5e5, 14 мая 2020 г.)

Подписано: Деррик Столи

line-log: интеграция с changed-path фильтрами Блума

При вычислении фильтра Блума с измененным путем нам нужно взять файлы, которые изменились из вычисления diff, и извлечь родительские каталоги. Таким образом, путь к каталогу, такой как Documentation, может соответствовать коммитам, изменяющим Documentation/git.txt.

Однако текущий код плохо справляется с этим процессом.

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

Правильно используйте hashmap_get() перед hashmap_add().
Также не забудьте включить функцию сравнения, чтобы их можно было правильно сопоставить.

Это влияет на тест в t0095-bloom.sh.
В этом есть смысл, внутри smallDir десять изменений, поэтому общее количество путей в фильтре должно быть 11.
В результате потребуется 11 * 10 битов. , а при 8 битах на байт получается 14 байтов.

В Git 2.28 (Q3 2020) git log -L... теперь использует преимущества того, какие пути затронуты этим коммитом? информация, хранящаяся в системе коммит-графа.


Для этого используется фильтр цветения.

См. commit f32dde8 (11 мая 2020 г.) от Деррик Столи (derrickstolee).
См. фиксация 002933f, https://github.com/git/git/commit/3cb9d2b6f9fd2dcb17f5534fd1536682e76f734a, commit d554672 (11 мая 2020 г.) от SZEDER32_Gábor (_.
(Объединено Junio ​​C Hamano - gitster - i n совершить c3a0282, 09 июня 2020 г.)

Подписано: Деррик Столи

commit-graph: упростите parse_commit_graph() # 1

Предыдущие изменения в механизме линейно-логарифмической обработки были направлены на то, чтобы первый результат появился быстрее. Это было достигнуто за счет того, что больше не просматривали всю историю коммитов перед возвратом первых результатов.
Есть еще один способ улучшить производительность: обход большинства коммитов намного быстрее. Давайте использовать фильтры Блума с измененным путем, чтобы сократить время, затрачиваемое на вычисление различий.

Поскольку вычисление line-log требует открытия больших двоичных объектов и проверки content-diff, существует еще много необходимых вычислений, которые нельзя заменить фильтрами Блума с измененным путем.
Часть, которую мы можем уменьшить, наиболее эффективна при проверке истории файл, который находится глубоко в нескольких каталогах, и эти каталоги часто модифицируются.
В этом случае вычисление для проверки того, является ли фиксация TREESAME своим первым родителем, занимает большую часть времени.
Это созрело. для улучшения с помощью фильтров Блума с измененным путем.

Мы должны убедиться, что prepare_to_use_bloom_filters() вызывается в revision.c, чтобы bloom_filter_settings были загружается в структуру rev_info из графа фиксации.
Конечно, некоторые случаи все еще запрещены, но в случае line-log pathspec предоставляется иначе, чем обычно.

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

Есть два случая, о которых следует позаботиться: коммиты слиянием и обычные коммиты.

Проверяя, может ли фильтр Bloom с измененным путем гарантировать TREESAME, мы можем избежать затрат на различие между деревьями. Если фильтр говорит, что, вероятно, изменилось, тогда нам нужно запустить tree-diff, а затем blob-diff, если было реальное редактирование.

  • Обычные коммиты имеют одинаковые условия, но в process_ranges_[merge|ordinary]_commit() методах они выполняются немного по-разному.
  • Первый - это случай всей истории, где мы выводим всю историю в /dev/null, чтобы увидеть, сколько времени потребуется для вычисления полной истории строчного журнала.

Репозиторий ядра Linux является хорошей площадкой для тестирования заявленных здесь улучшений производительности.
Есть два разных случая для тестирования:

Для проверки я выбрал пути, которые наиболее часто менялись в 10 000 самых популярных коммитов с помощью этой команды (украдено из StackOverflow):

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

что приводит к

git log --pretty=format: --name-only -n 10000 | sort | \
  uniq -c | sort -rg | head -10

(вместе с ложным первым результатом).
Похоже, что путь arch/x86/kvm/svm.c был переименован, поэтому мы игнорируем эту запись. Это оставляет следующие результаты для реального времени команды:

121 MAINTAINERS
 63 fs/namei.c
 60 arch/x86/kvm/cpuid.c
 59 fs/io_uring.c
 58 arch/x86/kvm/vmx/vmx.c
 51 arch/x86/kvm/x86.c
 45 arch/x86/kvm/svm.c
 42 fs/btrfs/disk-io.c
 42 Documentation/scsi/index.rst

Стоит отметить, что наименьшее ускорение происходит для файла MAINTAINERS, который:

|                              | Entire History  | First Result    |
| Path                         | Before | After  | Before | After  |
|------------------------------|--------|--------|--------|--------|
| MAINTAINERS                  | 4.26 s | 3.87 s | 0.41 s | 0.39 s |
| fs/namei.c                   | 1.99 s | 0.99 s | 0.42 s | 0.21 s |
| arch/x86/kvm/cpuid.c         | 5.28 s | 1.12 s | 0.16 s | 0.09 s |
| fs/io_uring.c                | 4.34 s | 0.99 s | 0.94 s | 0.27 s |
| arch/x86/kvm/vmx/vmx.c       | 5.01 s | 1.34 s | 0.21 s | 0.12 s |
| arch/x86/kvm/x86.c           | 2.24 s | 1.18 s | 0.21 s | 0.14 s |
| fs/btrfs/disk-io.c           | 1.82 s | 1.01 s | 0.06 s | 0.05 s |
| Documentation/scsi/index.rst | 3.30 s | 0.89 s | 1.46 s | 0.03 s |

Все это приводит к тому, что вы тратите больше времени на сравнение больших двоичных объектов и меньше времени на сравнение дерева.
Тем не менее, мы видим некоторые улучшения в этом случае и значительные улучшения в других случаях.
A 2 -4x ускорение, вероятно, более типичный случай, чем небольшое изменение на 5% для этого файла.

  • низко в иерархии каталогов, и
  • довольно большой файл.
  • Попробуйте git log как , как описано в документации git-log. Вам действительно нужно увидеть все 56000 коммитов в проект rails?

В Git 2.29 (4 квартал 2020 г.) фильтр Блума с измененным путем улучшен с использованием идей независимой реализации.


См. совершить 7fbfe07, совершить bb4d60e, 5cfa438, совершить 2ad4f1a, фиксация fa79653, совершить 0ee3cb8, совершить 1df15fref, < "https://github.com/git/git/commit/6141cdfdcbe9e3edf25467582c5f32658ae7 9f40 "rel =" nofollow noreferrer "> зафиксировать 6141cdf, зафиксировать cb9daf , совершить 35a9f1e (5 июня 2020 г.) от СЕДЕР Габор (szeder).
(Объединено Junio ​​C Hamano - gitster - в совершить de6dda0 a>, 30 июл 2020 г.)

Подписано: SZEDER Gábor
Подписано: Derrick Stolee

мы наблюдали увеличение скорости в 2-5 раз.

В то время как мы перебираем все записи таблицы поиска фрагментов, мы следим за тем, чтобы мы не пытались читать за пределами файла mmap-ed commit-graph, и проверяем на каждой итерации, что идентификатор фрагмента и смещение, которое мы собираемся чтение все еще находится в области памяти mmap-ed. Однако эти проверки на каждой итерации на самом деле не нужны, потому что количество фрагментов в файле графа фиксации уже известно до этого цикла из только что проанализированного заголовка графа фиксации.

Итак, давайте проверим, что файл графика фиксации достаточно велик для всех записей в таблице поиска фрагментов, прежде чем мы начнем итерацию по этим записям, и отбросим эти проверки для каждой итерации.
При этом учитывайте размер все, что необходимо для правильного файла графика фиксации, то есть размер заголовка, размер обязательного фрагмента OID Fanout, а также размер подписи в трейлере.

Обратите внимание, что это также требует изменения сообщения об ошибке. Se

И commit-graph:

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

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

revision.c: используйте фильтры Блума для ускорения обходов версий на основе пути

person VonC    schedule 02.05.2020