Ninja: Возможность использовать характеристики файла вместо временных меток

Созданный на 14 авг. 2018  ·  15Комментарии  ·  Источник: ninja-build/ninja

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

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

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

Самый полезный комментарий

Я также использовал хеширование на работе с большим успехом. Он основан на #929, но с кучей патчей, как видно на https://github.com/moroten/ninja/commits/hashed. hash_input = 1 на выбранных правилах очень удобно. Моя ветка все еще содержит ошибку, из-за которой файлы слишком часто stat , O(n^2) вместо O(n) . Ошибка связана с фальшивыми краями.

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

Другая идея состоит в том, чтобы сделать хэширование первоклассным членом ниндзя, т.е. переместить хэши в журнал сборки. Использование SHA256 станет одним из шагов к добавлению поддержки API удаленного выполнения Bazel. Реализацию C++ можно найти по адресу https://gitlab.com/bloomberg/recc/. Разве это не было бы очень мило?

К сожалению, устранение семантики фальшивых границ, вероятно, нарушит обратную совместимость.

Все 15 Комментарий

929 имеет реализацию. Несмотря на то, что он успешно используется (в форке) для тысяч сборок ежедневно, он не рассматривался для слияния.

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

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

Отвечая на однопоточный аргумент: Да, он добавляет инструкции в однопоточный цикл. На самом деле это имеет значение только в том случае, если один поток получает больше работы, чем может обработать (т. е. больше правил завершается, чем может обработать один поток (depslog+hashlog+...)). Только тогда перемешивание причиняет боль. В противном случае цикл с одним потоком все равно ожидает завершения заданий. Мы ни разу не видели занятого ниндзя с хешированием даже при экспериментах с -j1000. (А для быстрых правил хэширование в любом случае не интересно для экономии времени.)

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

Наконец, реализация в # 929 является добровольной и предоставляется бесплатно для людей, не использующих эту функцию (кроме оператора if).

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

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

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

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

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

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

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

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

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

Я также использовал хеширование на работе с большим успехом. Он основан на #929, но с кучей патчей, как видно на https://github.com/moroten/ninja/commits/hashed. hash_input = 1 на выбранных правилах очень удобно. Моя ветка все еще содержит ошибку, из-за которой файлы слишком часто stat , O(n^2) вместо O(n) . Ошибка связана с фальшивыми краями.

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

Другая идея состоит в том, чтобы сделать хэширование первоклассным членом ниндзя, т.е. переместить хэши в журнал сборки. Использование SHA256 станет одним из шагов к добавлению поддержки API удаленного выполнения Bazel. Реализацию C++ можно найти по адресу https://gitlab.com/bloomberg/recc/. Разве это не было бы очень мило?

К сожалению, устранение семантики фальшивых границ, вероятно, нарушит обратную совместимость.

Тем временем я придумал решение для моего варианта использования переключения веток в Chromium (что особенно болезненно); небольшая программа и скрипт Go, которые я использую, находятся здесь: https://github.com/bromite/mtool

Не стесняйтесь адаптировать его к своим вариантам использования, если он работает для Chromium, я уверен, он будет работать и для небольших проектов сборки (и для моих прогонов требуется незначительное время). Единственным недостатком является то, что если вы используете сценарий, который я опубликовал там, как есть, он будет засорять файлы .mtool повсюду в каждом родительском каталоге репозитория git, но ничто глобальное gitignore не может вылечить.

Интересно отметить, что я использую вывод git ls-files --stage для нужд хеширования; можно (но менее эффективно) попросить git хешировать также неиндексированные файлы, если ваша сборка зависит от них.

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

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

Командная строка уже хеширована ниндзя и сохранена в журнале сборки.

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

Это было бы очень неправильно. Сравнение хэшей можно исключить, если временные метки не совпадают; система сборки может предположить, что зависимость была изменена, и если она на самом деле не была изменена (только затронута), то сборка будет неоптимальной, но правильной. Однако, если временные метки совпадают, все еще возможно, что зависимость была изменена, и ее временная метка была принудительно сброшена (EDIT: или что цель была затронута и, таким образом, стала новее, чем ее зависимости). Без двойной проверки путем сравнения хэшей это приведет к неправильной сборке.

Я предполагаю, что Ninja уже предполагает и пропускает всю работу, когда временные метки совпадают. Так что в вашем примере это все равно будет неправильной сборкой.

Я не знаю (вероятно, из-за моего невежества) какого-либо инструмента, который производил бы другой вывод и сохранял бы предыдущую отметку времени. Зачем это делать?

ИМХО, пропуск проверки хэша при совпадении временных меток является очень правильной оптимизацией.

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

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

@rulatir Думаю, я в основном понимаю, о чем вы говорите :) Я думаю, это одна из причин, по которой Bazel и другие системы сборки, основанные на хеш-проверках, действительно против целевых выходов в дереве.

Хотя разве эта проблема не была бы решена, если бы система сборки проверяла, являются ли цели более новыми, чем ранее известное время, и при необходимости перестраивала?

@rulatir Думаю, я в основном понимаю, о чем вы говорите :) Я думаю, это одна из причин, по которой Bazel и другие системы сборки, основанные на хеш-проверках, действительно против целевых выходов в дереве.

Как хэш зависит от того, где находится файл?

Хотя разве эта проблема не была бы решена, если бы система сборки проверяла, являются ли цели более новыми, чем ранее известное время, и при необходимости перестраивала?

Я понимаю, что основное преимущество использования временных меток заключается в том, чтобы избежать необходимости поддерживать отдельную базу данных, которая отслеживает «ранее известные» сигнатуры версий. Если вы готовы отказаться от этого преимущества, почему эти подписи не должны быть хэшами?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги