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:

  1. Официальная версия CMake 3.2.2:

    $ time -p cmake -G "MSYS Makefiles" ..
    [...]
    real 43.93
    user 0.00
    sys 0.03
    
  2. С помощью 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
    
  3. С помощью 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_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
Другие вопросы по тегам