cmake rebuild_cache для * просто * подкаталога?
У меня есть проблема с медленной стадией генерации make-файлов CMake, которая похожа на этот вопрос без ответа:
CMake медленно генерирует make-файлы
Мой проект состоит из верхнего уровня CMakeLists.txt
файл, который использует add_subdirectory()
добавлять различные подпроекты для отдельных библиотек и исполняемых компонентов.
Для данного компонента CMakeLists.txt
Файл содержит что-то вроде:
add_library(mylib SHARED
sourceFile1.cpp
sourceFile2.cpp
...
)
Я могу собрать только содержимое этого каталога, используя:
make mylib
Если я изменю CMakeLists.txt
файл в подкаталоге (который я много делал как часть перехода с чистых Makefiles на CMake) затем запустите make
он корректно перезапускает CMake для обновления конфигурации, как если бы я запускал make rebuild_cache
,
Тем не менее, я заметил, что на самом деле это перенастраивает весь проект. Я действительно хочу, чтобы CMake был достаточно умен, чтобы знать, что ему нужно только восстановить Makefile в текущем каталоге и подкаталогах.
Есть ли лучший способ структурировать проект CMake для достижения этой цели? Я вижу, что некоторые люди используют project() для каждого CMakeLists.txt в каждом подпроекте. Вообще это хорошая идея?
В качестве альтернативы / дополнительно есть ли какой-нибудь способ ускорить этап генерации CMake? (сейчас у меня 60 с +)
Бонусные баллы, если вы хотите обсудить, почему сам CMake должен или не должен работать параллельно (представьте cmake -j
).
Я добавил тэг мезонов в качестве скромной награды, но сам по себе он еще не привлек достаточного внимания, чтобы оправдать ответ. Это такая проблема, которая может заставить людей перейти на сборку систем на мезон-сборку (при условии, что у нее нет похожих проблем) или что-то подобное.
Возможно, правильный ответ - это невозможно сделать без изменения исходного кода на CMake. Чтобы получить награду, мне нужно объяснить, как работает CMake и / или где он имеет недостатки.
Пояснение: в моем случае это медленный шаг генерации. Сама конфигурация достаточно быстрая, но CMake довольно долго зависает между выводом "- Конфигурирование выполнено" и "- Генерация завершено".
Для полной перестройки кэша я запускаю:
make -n rebuild_cache
Запуск CMake для восстановления системы сборки... используя генератор Makefile - FOOBAR_VERSION: 01.02.03 - Настройка выполнена - Генерация завершена - Файлы сборки были записаны в: / home / brucea / work / depot / emma / main / cmake реальный 74,87 пользователь 1.74 система 1.02
Под капотом это работает:
cmake -H<source dir> -B<build dir>
я предполагаю -B
это синоним --build
, Ни одна из этих опций не описана правильно в документации. -H
является корнем исходного каталога (не совпадает с --help
как документация заставит вас поверить).
Быстро добраться до вывода "Конфигурирование выполнено", но медленно оттуда:
Например,
15:44:14 execve ("/ usr / local / bin / cmake", > Создание grep cmake_strace.log >grep "Настройка" cmake_strace.log 15:44:15 write(1, "- Конфигурирование выполнено \n", 20-- Конфигурирование выполнено 15:45:01 write(1, "- Генерация завершена \n", 19-- Генерация завершена > grep "Сборка файлов" cmake_strace.log 15:45:22 write(1, "- Файлы сборки были записаны"..., 77-- Файлы сборки были записаны в:
Если редактировать один файл CMakeLists.txt в подкаталоге, а затем запустить make -n
работает:
cd /home/project/cmake && /usr/local/bin/cmake -H/home/project/cmake -B/home/project/cmake --check-build-system CMakeFiles/Makefile.cmake 0
--check-build-system - еще одна недокументированная опция.
Эффект тот же - восстановить всю систему сборки, а не только текущее поддерево. Нет различий в поведении между сборкой из источника и вне источника.
Если я запускаю трассировку, например:
strace -r cmake --trace -H/home/project/cmake -B/home/project/cmake 2>&1 | tee cmake_rebuild_cache.log
sort -r cmake_rebuild_cache.log | uniq
Большая часть времени, по-видимому, тратится на (или между) вызовами open, access и unlink.
Длина каждой задачи довольно переменна, но их огромное количество накапливается. Я понятия не имею, что такое файлы Labels.json и Labels.txt (что-то внутреннее для CMake).
Один забег:
49.363537 open ("/home/projectbar/main/ test / foo2bar /CMakeFiles/ test2.foo2bar.testViewingSource1.dir / build.make", O_RDONLY) = 5 1.324777 доступ ("/home/projectbar/main/test/performance/CMakeFiles/performancetest.chvcthulhu.testChvcthulhuPerformance2.dir", R_OK) = 0 0,907807 доступ ("/home/projectbar/main/test/foo2bar/CMakeFiles/test2.foo2bar.testPeripheralConnection2.dir", R_OK) = 0 0 02727 /projectbar/main/src/foo2bar/Foo2Bar/CMakeFiles/foo2bar_lib.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0,600272 доступ ("/home/projectbar/main/test/foo2bar/testFilesModel2.ok", R_OK) = 0 0,599010 доступ ("/home/projectbar/main/test/hve2snafu/testI nvalidByte2c.ok", R_OK) = 0 0,582466 чтение (5, " версия openjdk \"1.8.0_71\"\nOpenJ"..., 1024) = 130 0,570540 writev(3, [{"# CMAKE-сгенерированный файл: НЕ ЭТО "..., 8190}, {"M", 1}], 2) = 8191 0,553576 close(4) = 0 0.448811 unlink("/home/projectbar/main/test/snafu2hve/CMakeFiles/test2.snafu2hve.testNoProbes2.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0,431559 доступ ("/home/projectbar/main/src/foo2bar/Foo2Bar/CMakeFiles/foo2bar_lib.dir", R_OK) = 0 0,408003 unlink("/home/projectbar/main/test/lachesis/CMakeFiles/test2.lachesis.testBadSequenceNumber1.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0.407120 write(4, "# Набор языков для которых "..., 566) = 566 0,406674 write(3, "# CMAKE-генерируемый файл: НЕ E"..., 675) = 675 0.383892 read(3, "ewingPeriod.cpp.o -c /home/bruce"..., 8191) = 8191 0.358490 unlink("/home/projectbar/main/cmake/CMakeFiles/mklinks.chvdiff.dir/progress.make.tmp") = -1 ENOENT (такого файла или каталога нет)
Еще один прогон той же команды:
2.009451 unlink ("/home/projectbar/main/ cmake /CMakeFiles/ mklinks.lachesis.dir / Labels.json") = -1 ENOENT (нет такого файла или каталога)) = 20) = 19 1.300387 доступ ("/home/projectbar/main/test/chvedit/CMakeFiles/test2.chvedit.tefooultiMatchFactoringEdit2.dir", R_OK) = 0 1.067957 доступ (" / home / projectbar / main / test / chvedit /CMakeFiles/ test2.chvedit.tefooultiMatchFactoringEdit2.dir "R) = 0) = 1 0.885854 unlink ("/home/projectbar/main/ src / gorkyorks2bar /CMakeFiles/ doxygen.correct.gorkyorks2bar.dir / Labels.json") = -1 ENOENT (нет такого файла или каталога) 0.854539 доступ ("/home/projectbar/main/test/reportImpressions/ReportImpressions/CMakeFiles/testsuite1_reportImpressions.dir", R_OK) = 0 0.791741 unlink ("/home/projectbar/main/ cmake /CMakeFiles/ mklinks.bar_models.dir / progressma.ma. tmp ") = -1 ENOENT (такого файла или каталога нет) 0.659506 unlink (" / home / projectbar / main / cmake /CMakeFiles/ mklinks.dir / progress.make.tmp ") = -1 ENOENT (такого файла или каталога нет) 0,647838 unlink ("/home/projectbar/main/ test / li byar / YarModels /CMakeFiles/ testsuite1_yarmodels.dir / Labels.txt ") = -1 ENOENT (нет такого файла или каталога) 0.620511 unlink (" / home / projectbar / main / test / libyar / YarModels /CMakeFiles/ testsuite1_yarmodels.dir / Labels.json ") = -1 ENOENT (такого файла или каталога нет) 0.601942 unlink (" / home / projectbar / main / cmake /CMakeFiles/ mklinks.lachesis.dir / Labels.txt ") = -1 ENOENT (такого файла нет или каталог) 0,591871 доступ ("/home/projectbar/main/ src / runbardemo / simple_demo / CMakeFiles", R_OK) = 0 0,582448 запись (3, "CMAKE_PROGRESS_1 = \n\n", 21) = 21 0,536947 запись (3, "CMAKE_PROGRESS_1 = \n\n", 21) = 21 0.499758 unlink("/home/projectbar/main/test/foo2bar/CMakeFiles/test2.foo2bar.testI nputDirectory1.dir/progress.make.tmp") = -1 ENOENT (нет такой файл или каталог) 0.458120 unlink("/home/projectbar/main/test/yak2dcs/CMakeFiles/test2.yak2dcs.testsuite2.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0.448104 unlink("/home/projectbar/main/test/reportImpressions/CMakeFiles/test2.reportImpressions.dir/progress.make.tmp") = -1 ENOENT (такого файла или каталога нет) 0.444344 доступ ("/home/projectbar/main/src/bananas/CMakeFiles/bin.bananas.dir", R_OK) = 0 0.442685 unlink("/home/projectbar/main/test/rvedit/CMakeFiles/test2.rvedit.tefooissingOptionValue.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0.425604 unlink("/home/projectbar/main/test/listdcs/CMakeFiles/test2.listdcs.testListCalls5.dir/progress.make.tmp") = -1 ENOENT (нет такого файла или каталога) 0.391163 доступ ("/home/projectbar/main/src/siedit/CMakeFiles/siedit.dir", R_OK) = 0 0.362171 доступ ("/home/projectbar/main/test/foo2bar/CMakeFiles/test2.foo2emma.testHowResults6.dir", R_OK) = 0
Обратите внимание, что генератор ниндзя намного быстрее (хотя все еще не блестящий). Например,
/usr/bin/time -p ninja rebuild_cache
ниндзя: предупреждение: несколько правил генерировать../src/ams2yar/ams2yar. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: несколько правил генерируют../src/vox/vox. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: генерируется несколько правил../src/bananas/bananas. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: несколько правил генерировать../src/fidlertypes2fidlerinfo/fidlertypes2fidlerinfo. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: несколько правил генерировать../src/mkrundir/mkrundir. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: несколько правил генерировать../src/runyar/runyar. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] ниндзя: предупреждение: генерируется несколько правил../src/runyardemo/runyardemo. сборки с этой целью не будут правильными; продолжая в любом случае [-w dupbuild=warn] [1/1] Запуск CMake для регенерации системы сборки... Генератор =Ninja - FOO_VERSION: 01.02.03 - Настройка выполнена - Генерация завершена - Файлы сборки были записаны в: /home/project/cmake/build настоящий 12.67 пользователь 1.01 система 0,31
Обратите внимание, что проект еще не совсем готов для Ninja, так как есть ошибки, такие как:
ninja: warning: multiple rules generate ../src/runfoobardemo/runfoobardemo. builds involving this target will not be correct; continuing anyway [-w dupbuild=warn]
а также
ninja: error: dependency cycle: ../src/foobar -> ../src/foobar/CMakeFiles/foobar -> ../src/ams2emma/foobar
быть решенным. Этот вопрос действительно о том, почему генератор Makefile работает медленно. Я не уверен, являются ли проблемы, которые показывает ниндзя, полезными подсказками здесь или красными сельдями.
Сборка CMake с большим количеством оптимизаций не помогает.
Исходя из моего вывода трассировки и выхода времени, маловероятно, что это произойдет. Время пользователя и, следовательно, время, потраченное внутри самого кода CMake, довольно мало. (см., например, Что означают "реальный", "пользователь" и "sys" в выводе времени (1)?).
Вот что я попробовал для полноты:
export CXX_FLAGS="-O3 -ftree-vectorise -msse2"
cmake -DCMAKE_BUILD_TYPE=RELEASE
На самом деле использование более оптимизированного CMake ускоряет процесс настройки, но в моем случае это медленная часть генерации. Из времени следует, что этот шаг как-то связан с вводом / выводом.
Я решил исследовать идею Флориана о том, что использование памяти в потоке файлов для временных файлов может иметь значение.
Я решил попробовать простой маршрут и взломал CMake, чтобы вместо этого записать файлы.tmp на RAM-диск.
Затем я пошел на попятную и попытался сгенерировать систему сборки на RAM-диске:
sudo mkdir /mnt/ramdisk
sudo mount -t tmpfs -o size=512m tmpfs /mnt/ramdisk
/usr/bin/time -p cmake -H/<source> -B/mnt/ramdisk/build
Я был очень удивлен, обнаружив, что это не имеет никакого значения для времени настенных часов:
real 59.61
user 1.55
sys 0.62
>du -sh /mnt/ramdisk/build/
4.4M /mnt/ramdisk/build/
Аналогично с ramfs:
real 51.09
user 1.58
sys 0.50
Что здесь может происходить? Я угадывал подпроцессы, но я не могу понять, какие подпроцессы потребляют время настенных часов, если они есть. Они выглядят очень недолго.
Для полноты вот некоторый вывод из perf (CMake построен с -fno-omit-frame-pointer
):
perf record -g --call-graph dwarf cmake -H<source> -B<build>
perf report -g graph
Образцы: 17K событий 'циклов', Количество событий (прибл.): 14363392067 Символ общей команды детей с собственной командой + 65,23% 0,00% cmake cmake [.] Do_cmake + 65.02% 0.00% cmake cmake [.] Cmake::Run + 60.32% 0,00% cmake cmake [.] Main + 59.82% 0.00% cmake libc-2.17.so [.] __Libc_start_main + 57.78% 0.00% cmake cmake [.] _Start + 55.04% 0.00% cmake cmake [.] CmGlobalUnixMakefileGenerator3::Generate + 54,56% 0,00% cmake cmake [.] Cmake::Generate + 49,90% 0,00% cmake cmake [.] CmGlobalGenerator::Generate + 38,87% 0,02% cmake cmake [.] См LocalUnixMakefileGenerator3::Generate + 18,65% 0,01% cmake cmake [.] cmMakefileTargetGenerator::WriteTargetBuildRules + 17,05% 0,02% cmake cmake [.] cmMakefile::ExecuteCommand + 16,99% 0,01% cmake cmake [.] cmMakefile::ReadListFile + 16,84% 0,01% cmake cmake [.] I nSom.7::% I nCom::9ComCom 0,00% cmake cmake [.] CmMakefile:: Configure + 14,71% 0,00% cmake cmake [.] CmMakefile::ConfigureSubDirectory + 14,67% 0,05% cmake cmake [.] CmMacroHelperCommand::InvokeInitialPass + 14,27% 0,02% cmake cmake [.] cmMakefileUtilityTargetGenerator::WriteRuleFiles + 13,91% 0,00% cmake cmake [.] cmGlobalGenerator:: Конфигурировать + 13.50% 0,05% cmake cmake [.] cmOutputConverter:: cmake + 13%% преобразования [.] cmAddSubDirectoryCommand::InitialPass + 13,46% 0,00% cmake cmake [.] cmMakefile::AddSubDirectory + 12,91% 0,00% cmake cmake [.] cmGlobalUnixMakefileGenerator3::Configure + 12,82% 0,00% cmake cmake::. 10,90% 0,00% cmake cmake [.] Cmake:: Configure + 10,55% 0,02% cmake cmake [.] CmMakefileTargetGenerator::WriteObjectRuleFiles + 10,35% 0,09% cmake cmake [.] CmLocalUnixMakefileGenerator3::Write.7Make0%. [%.MakeMule.] cmMakefileTargetGenerator::WriteObjectBuildFile + 7,97% 0,00% cmake cmake [.] cmMakefileLibraryTargetGenerator::WriteRuleFiles + 7,93% 0,00% cmake cmake [.] cmMakefileExecutableTargetGenerator::WriteMakefile0.0MakeFileMakeFileMakeFileMakeFile_MileFile_MileFile_MileFile_MileFileFile_MileFile_MileFile_MileFileFileFileFileMileFileFileCameCileMileFileFileCameCheileFileCheileFileCheileFileCheileFileCheileFileCheileFileCheileMileFileCheileFileCheileFileCheileFileCheileFileCheileFileCheileFileCheileFileTygileMeCeileMeCheileFileMeCeileCileCheileFileMakeFileMeMe обратно 0.02% cmake [kernel.kallsyms] [k] sysret_audit + 7,60% 0,05% cmake [kernel.kallsyms] [k] __audit_syscall_exit + 7,40% 0,08% cmake cmake [.] Cmsys::SystemTools::CollapseFullPath
И отчет о перфекте -g графа -no-children:
+ 2.86% cmake libc-2.17.so [.] _I nt_malloc + 2,15% cmake libc-2.17.so [.] __Memcpy_ssse3_back + 2.11% cmake [kernel.kallsyms] [k] find_next_bit + 1,84% cmake libc-2.17.so [.] __Memcmp_sse4_1 + 1,83% cmake libc-2.17.so [.] _I nt_free + 1.71% cmake libstdC++. So.6.0.20 [.] Std::__ostream_insert > + 1.18% cmake libstdC++. So.6.0.20 [.] Std::basic_string, std::allocator >::~basic_string + 1.13% cmake libc-2.17.so [.] Malloc + 1,12% cmake cmake [.] CmOutputConverter::Shell__ArgumentNeedsQuotes + 1,11% cmake libstdC++. So.6.0.20 [.] Std::string::compare + 1.08% cmake libc-2.17.so [.] __Strlen_sse2_pminub + 1,05% cmake cmake [.] Std::string::_S_construct + 1,04% cmake cmake [.] Cmsys::SystemTools::ConvertToUnixSlashes + 0,97% cmake cmake [.] Yy_get_previous_state + 0,87% cmake cmake [.] CmOutputConverter::Shell__GetArgument + 0,76% cmake libstdC++. So.6.0.20 [.] Std::basic_filebuf >::xsputn + 0,75% cmake libstdC++. So.6.0.20 [.] Std::string::size + 0,75% cmake cmake [.] CmOutputConverter::Shell__SkipMakeVariables + 0,74% cmake cmake [.] CmOutputConverter::Shell__CharNeedsQuotesOnUnix + 0.73% cmake [kernel.kallsyms] [k] mls_sid_to_context + 0,72% cmake libstdC++. So.6.0.20 [.] Std::basic_string, std::allocator >::basic_string + 0,71% cmake cmake [.] CmOutputConverter::Shell__GetArgumentSize + 0,65% cmake libc-2.17.so [.] Malloc_consolidate + 0,65% cmake [kernel.kallsyms] [k] mls_compute_context_len + 0,65% cmake cmake [.] CmOutputConverter::Shell__CharNeedsQuotes + 0,64% cmake cmake [.] CmSourceFileLocation::Matches + 0,58% cmake cmake [.] CmMakefile::ExpandVariablesInStringNew + 0.57% cmake cmake [.] Std::__deque_buf_size + 0,56% cmake cmake [.] CmCommandArgument_yylex + 0,55% cmake cmake [.] Std::vector >::size + 0,54% cmake cmake [.] Cmsys::SystemTools::SplitPath + 0.51% cmake libstdC++. So.6.0.20 [.] Std::basic_streambuf >::xsputn
1 ответ
Существует так много аспектов, которые определяют конфигурацию CMake и продолжительность шагов генерации (помимо того, что вы на самом деле делаете в файлах CMakeLists.txt; это, например, ваша хост-система, ваш набор инструментов и какая версия / дистрибутив CMake вы используете).
Поэтому я стараюсь сосредоточиться на конкретных вопросах, которые у вас есть.
Пересобрать / переписать make-файлы только для подкаталога?
Для начала: используя add_subdirectory()
хорошо для структурирования вашего кода CMake. Но вы должны иметь в виду, что вы всегда можете изменить глобальные свойства CMake в подкаталоге, и что цели внутри этих подкаталогов могут иметь взаимные зависимости.
Так, что делает CMake (учитывая, что я коснулся одного CMakeLists.txt
файл в подкаталоге "дело обсуждается здесь):
- Если
CMakeLists.txt
файл изменяется, он проходит через полную иерархиюCMakeLists.txt
файлы снова и перестраивает среду сборки снова в памяти. - Теперь он временно воссоздает все необходимые файлы build/make и проверяет, не отличаются ли они от существующих (см.
cmGeneratedFileStreamBase::Close()
). - Если файл изменился, он заменяет существующий новым.
Такое поведение необходимо, потому что любой make-файл может измениться, даже если только подкаталог CMakeLists.txt
файл был изменен и оптимизирован для предотвращения ненужных перестроений во время make
шаг (из затронутых make-файлов).
Есть ли способ ускорить шаг генерации CMake?
Так что да, он временно переписывает все make-файлы (что может быть медленным), и нет, вы не можете минимизировать это при использовании add_subdirectory()
только в измененный подкаталог.
Возможно, одной из возможных оптимизаций производительности в будущем в собственном коде CMake будет использование потоков памяти вместо файловых потоков для временных файлов.
@BruceAdams проверил это, используя RAM-диск для сгенерированной среды make-файлов без эффекта.
И да, CMake сгенерировал cmake_check_build_system
Правило делает почти так же, как rebuild_cache
Правило и да, используемые -B
, -H
а также --check-build-system
параметры - это параметры внутренней командной строки CMake, и поэтому они недокументированы (даже если на них часто ссылаются при переполнении стека, например, в одном из моих ответов здесь).
Что помогло мне ускорить настройку / генерацию, так это перестроить сам CMake с гораздо большим количеством опций оптимизации, чем в обычных дистрибутивах, и использовать 64-битный набор инструментов вместо 32-битных версий, которые еще распространяются в настоящее время.
Вот некоторые результаты тестирования (с использованием сценария тестирования CMake, приведенного ниже со 100 подкаталогами / библиотеками) на моем ПК с Windows, всегда использующим одну и ту же среду MSYS, но разные компиляции CMake с одним и тем же исходным кодом CMake:
Официальная версия CMake 3.2.2:
$ time -p cmake -G "MSYS Makefiles" .. [...] real 43.93 user 0.00 sys 0.03
С помощью
mingw32
а такжеGNU 4.8.1
Я перестраиваю CMake 3.2.2 сcmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3" -G "MSYS Makefiles" ..
и получил
$ time -p /c/temp/cmake-3.2.2/MSYS32/bin/cmake.exe -G "MSYS Makefiles" .. [...] real 41.37 user 0.01 sys 0.04
И то же самое с моим выключенным антивирусом:
$ time -p /c/temp/cmake-3.2.2/MSYS32/bin/cmake.exe -G "MSYS Makefiles" .. [...] real 20.98 user 0.00 sys 0.04
С помощью
mingw-w64
а такжеGNU 5.3.0
Я перестраиваю CMake 3.2.2 с$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -m64 -Ofast -flto" -G "MSYS Makefiles" ..
и получил
$ time -p /c/temp/cmake-3.2.2/MSYS64/bin/cmake.exe -G "MSYS Makefiles" .. [...] real 25.59 user 0.00 sys 0.04
И то же самое с моим выключенным антивирусом:
$ time -p /c/temp/cmake-3.2.2/MSYS64/bin/cmake.exe -G "MSYS Makefiles" .. [...] real 6.95 user 0.00 sys 0.03
Подводя итог, я вижу два основных влияния:
1-й: шаг конфигурации можно ускорить, перейдя на 64-битную версию и оптимизировав для своей процессорной платформы (вам, безусловно, придется найти общую базу -march=...
или же -mtune=...
для всех ПК вашего проекта).
2-й: шаг генерации можно в основном ускорить путем поиска возможных узких мест файлового ввода-вывода за пределами CMake. В моем случае, указание антивирусному программному обеспечению не проверять набор инструментов и создавать каталоги каждый раз, когда я читаю / записываю в них, действительно ускоряло процесс.
Примечание: я подтвердил результаты теста @BruceAdams, что автоматическая векторизация компилятора (по умолчанию для -O3
или же -Ofast
) не в состоянии многое сделать для способности исходного кода CMake работать в нескольких процессах / на нескольких ядрах.
Есть ли лучший способ структурировать проект CMake для достижения этой цели?
Да, если вы, например, знаете, что определенное поддерево вашего кода сценария CMake просто генерирует библиотеку и не имеет зависимостей, вы можете поместить эту часть во внешний проект, используя ExternalProject_Add()
, И да, с аналогичными проблемами в отношении крупных проектов CMake, это рассматривается как хорошая практика "современного CMake" (см. Также ссылки ниже).
Рекомендации
- Советы по производительности CMake
- CMake: параллельная генерация make-файлов
- GCC: чем март отличается от mtune?
- CMake: Как настроить зависимости исходного кода, библиотеки и CMakeLists.txt?
- Обеспечение доступа к библиотеке CMake другими пакетами CMake автоматически
- Используйте библиотеки с поддержкой CMake в вашем проекте CMake
Что я использовал для воспроизведения вашей проблемы
Просто для полноты, и если кто-то хочет проверить эти цифры по своим собственным, вот мой тестовый код:
cmake_minimum_required(VERSION 3.0)
project(CMakeTest CXX)
#set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1)
set(_idx 1)
while (_idx LESS 100)
math(EXPR _next_idx "${_idx} + 1")
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib${_idx}")
file(MAKE_DIRECTORY "lib${_idx}")
file(
WRITE "lib${_idx}/lib${_idx}.h"
"int lib${_idx}_func();"
)
file(
WRITE "lib${_idx}/lib${_idx}.cc"
"#include \"lib${_next_idx}.h\"\n"
"int lib${_idx}_func() { return lib${_next_idx}_func(); }"
)
file(
WRITE "lib${_idx}/CMakeLists.txt"
"add_library(lib${_idx} \"lib${_idx}.cc\")\n"
"target_link_libraries(lib${_idx} lib${_next_idx})\n"
"target_include_directories(lib${_idx} PUBLIC \".\")"
)
endif()
add_subdirectory("lib${_idx}")
set(_idx "${_next_idx}")
endwhile()
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib${_idx}")
file(MAKE_DIRECTORY "lib${_idx}")
file(
WRITE "lib${_idx}/lib${_idx}.h"
"int lib${_idx}_func();"
)
file(
WRITE "lib${_idx}/lib${_idx}.cc"
"int lib${_idx}_func() { return 0; }"
)
file(
WRITE "lib${_idx}/CMakeLists.txt"
"add_library(lib${_idx} \"lib${_idx}.cc\")\n"
"target_include_directories(lib${_idx} PUBLIC \".\")"
)
endif()
add_subdirectory("lib${_idx}")
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/main.cc")
file(
WRITE "main.cc"
"#include \"lib1.h\"\n"
"int main() { return lib1_func(); }"
)
endif()
add_executable(${PROJECT_NAME} "main.cc")
target_link_libraries(${PROJECT_NAME} lib1)
А потом - после первого cmake ..
а также make
звонки - делаю:
$ touch ../lib100/CMakeLists.txt
$ time -p cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: [your path here]
real 28.89
user 0.01
sys 0.04