Как выразить зависимости PGO в CMake 3.7?
У меня есть программа на C++, которую я создаю с помощью функции оптимизации профиля Clang 3.9. Вот что должно произойти:
- Я строю программу с включенными инструментами.
- Я запускаю эту программу, создавая файл с данными профиля:
prof.raw
, - я использую
llvm-profdata
преобразоватьprof.raw
в новый файл,prof.data
, - Я создаю новую сборку этой же программы с несколькими изменениями:
- При компиляции каждого файла.cpp в файл.o я использую флаг компилятора
-fprofile-use=prof.data
, - При связывании исполняемого файла я также указываю
-fprofile-use
,
- При компиляции каждого файла.cpp в файл.o я использую флаг компилятора
У меня есть Gnu Makefile для этого, и он прекрасно работает. Моя проблема возникает сейчас, когда я пытаюсь портировать этот Makefile на CMake (3.7, но я могу обновить). Мне нужно решение для работы (по крайней мере) с генератором Makefiles Unix, но в идеале оно будет работать для всех генераторов.
В CMake я определил две исполняемые цели: foo-gen
а также foo-use
:
- когда
foo-gen
выполняется, это создаетprof.raw
файл. - я использую
add_custom_command
создать правило для созданияprof.data
отprof.raw
,
Моя проблема в том, что я не могу понять, как сказать CMake, что каждый из объектных файлов зависит от foo-use
имеет зависимость от файла prof.data
,
Самая многообещающая идея, которую я имел, состояла в том, чтобы (1) найти способ перечислить все
.o
файлы, на которыхfoo-use
зависимости, а затем (2) перебрать каждый из этих.o
файлы, звонящиеadd_dependency
для каждого.Проблема с этим подходом состоит в том, что я не могу найти идиоматический способ, в моем файле CMakeLists.txt, перечислить список объектных файлов, от которых зависит исполняемый файл. Это может быть открытой проблемой с CMake.
Я также подумал об использовании
set_source_files_properties
установитьOBJECT_DEPENDS
недвижимость на каждом из моих.cpp
файлы, используемыеfoo-use
, добавивprof.data
в список этого свойства.Проблема с этим (AFAICT) заключается в том, что каждый из моих
.cpp
файлы используются для создания двух разных.o
файлы: один дляfoo-gen
и один дляfoo-use
, Я хочу.o
файлы, которые связаны вfoo-use
иметь эту зависимость во время компиляции отprof.data
; но.o
файлы, которые связаны вfoo-gen
не должен зависеть от времени компиляцииprof.data
,И AFAIK,
set_source_files_properties
не позволяет мне установитьOBJECT_DEPENDS
свойство иметь одно из двух значений, в зависимости от того,foo-gen
или жеfoo-use
текущая цель интереса.
Какие-нибудь предложения для чистого (ish) способа сделать эту работу?
1 ответ
Обсуждение авторской идеи №1
Самая многообещающая идея, которая у меня была, состояла в том, чтобы (1) найти способ перечислить все файлы, от которых зависит, а затем (2) перебрать каждый из этих файлов, вызвав
для каждого.
Это не должно работать в соответствии с документацией дляadd_dependencies
, в котором говорится:
Делает верхний уровень зависимым от других целей верхнего уровня , чтобы убедиться, что они строятся раньше.
Т.е. Вы не можете использовать его, чтобы цель зависела от файлов — только от других целей .
Обсуждение авторской идеи №2
Я также рассматривал возможность использования set_source_files_properties для установки свойства для каждого из моих файлов, используемых , добавляя в список этого свойства.
Проблема с этим (AFAICT) в том, что каждый из моих
files используется для создания двух разных файлов: одного для и одного для . Я хочу, чтобы файлы, на которые ссылаются, имели эту зависимость времени компиляции от ; но файлы, на которые ссылаются, не должны иметь зависимости во время компиляции от . И, как ни крути,
не позволяет мне установить для свойства одно из двух значений в зависимости от того, или является текущей интересующей целью.
В разделе комментариев вы упомянули, что могли бы решить эту проблему, если бы поддерживали выражения генератора, но это не так. В качестве примечания: в репозитории CMake gitlab есть отслеживание проблемы. Вы можете поставить лайк и описать свой вариант использования для справки.
В разделе комментариев вы также упомянули возможное решение этой проблемы:
Возможное другое решение: а) система двойного проекта, в которой основной пользователь, вызываемый проектом, пересылает настройки второму проекту pgo, снова компилируя те же настройки.
На самом деле вы можете поместить это в проект CMake, чтобы он стал частью сгенерированной системы сборки: сделайте так, чтобы проект верхнего уровня включал себя как внешний проект. Внешнему проекту можно передать переменную кэша, чтобы настроить его как
Говоря по опыту, это совсем другая кроличья нора, состоящая из длинных сеансов чтения и проверки документации CMake, если вы никогда раньше не вызывали и не делали ничего с этим вручную, поэтому этот ответ может относиться к новому вопросу, посвященному ему.
Это может решить проблему отсутствия выражений генератора в
Предложенное решение
Вот что я нашел, чтобы заставить исходники перекомпилировать при изменении данных профиля:
- К пользовательской команде, которая запускает исполняемый файл обучения, создает и переформатирует данные обучения, добавьте еще одну
который создает заголовочный файл С++, содержащий метку времени в комментарии. - Включите этот заголовок во все исходники, которые вы хотите перекомпилировать, если обучение было повторено.
Если вы хотите поддерживать сборки без PGO, оберните заголовок временной метки в заголовок, который проверяет его существование с помощью
Я почти уверен, что при таком подходе CMake не выполняет проверку зависимостей TU от данных профиля, и вместо этого это работает отслеживание зависимостей заголовков сгенерированной системы сборки. Обоснование включения комментария временной метки в заголовочный файл вместо того, чтобы просто «прикоснуться» к нему для изменения временной метки в файловой системе, заключается в том, что сгенерированная система сборки может обнаруживать изменения по содержимому файла, а не по временной метке файловой системы.
Все недостатки предлагаемого решения
До боли очевидная слабость этого подхода заключается в том, что вам нужно добавить заголовок во все файлы .cpp, которые вы хотите проверить на повторную компиляцию. Из этого может возникнуть несколько проблем (от наименее до наиболее вопиющих):
Возможно, вам это не понравится с эстетической точки зрения.
Это, безусловно, открывает дыру для человеческой ошибки, если вы забываете включить заголовок для новых файлов .cpp.
Я не знаю, как это решить.В некоторых компиляторах есть флаг, который можно использовать для включения файла в каждый исходный файл, например, в GCC.флаг . Затем вы можете просто добавить этот флаг к цели CMake, используяtarget_compile_options(<target> PRIVATE "SHELL:-include <path>")
Возможно, вы не сможете изменить некоторые исходные коды, которые необходимо перекомпилировать, например исходные коды из сторонних статических библиотек, от которых зависит ваша библиотека. Могут быть обходные пути, если вы используете
делая что-то с шаг, но я не знаю.
Для моего личного проекта приемлемы #1 и #2, а #3 не является проблемой.