MSVC10 /MP строит не многоядерные папки в проекте

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

При компиляции проекта с /MP кажется, что только файлы в одной папке компилируются одновременно. Я использовал Process Explorer, чтобы провести пальцем по командной строке и подтвердить поведение.

Фильтры проекта, похоже, не оказывают никакого влияния на то, что компилируется одновременно.

Структура проекта на диске:

Folder\
  project.vcxproj  
  source\  
    foo.cpp  
    foo1.cpp  
  other_folder\  
    bar.cpp
    bar1.cpp
    bar3.cpp

Начальное дерево процессов:

MSBuild.exe
  cl.exe  ( passed: source\foo.cpp source\foo1.cpp )
    cl.exe  ( passed: source\foo.cpp )
    cl.exe  ( passed: source\foo1.cpp )

После завершения 2 дочерних экземпляров cl.exe родительский элемент закрывается, и появляется следующее дерево процессов:

MSBuild.exe
  cl.exe  ( passed: other_folder\bar.cpp other_folder\bar1.cpp other_folder\bar2.cpp )
    cl.exe  ( passed: other_folder\bar.cpp )
    cl.exe  ( passed: other_folder\bar1.cpp )
    cl.exe  ( passed: other_folder\bar2.cpp )

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

3 ответа

Решение

Использование%(RelativeDir) в проекте "Имя файла объекта" (в XML-файле vcxproj, /Fo в командной строке CL.exe) заставляет msbuild пакетировать файлы cpp в cl.exe для каждого каталога. Это может оказать значительное влияние на выгоды, полученные от использования /MP.

Обратите внимание, что если ваш проект использует%(RelativeDir) для объектных файлов, вполне вероятно, что конфигурация пытается избежать столкновения.obj файлов, которые из файлов cpp с одинаковыми именами в разных папках.

Параметр командной строки / Fo, как правило, представляет собой папку, в которую компилятор сбрасывает файлы obj - передается только ОДИН, поэтому все файлы cpp для данного каталога могут передаваться только в CL.exe одновременно.

Это было больно - но я рад, что есть причина и решение. Надеюсь, поможет.


Обновить

Товарищ по команде обнаружил, что каждый раз, когда параметр MSBuild отправляется в CL.exe, он, кажется, ломается или строго ограничивает /MP. Это наиболее вероятно, потому что / MP для нормальной работы CL.exe верхнего уровня должен иметь пакет файлов cpp.

Наше решение состояло в том, чтобы не использовать какие-либо параметры msbuild (я думаю, что это%params%) для "Имя файла объекта". Это потребовало, чтобы мы переименовали некоторые файлы cpp, чтобы они не конфликтовали.

Надеюсь, что это изменилось в VS2012 или VS2013.

Я обратился к этой проблеме здесь с новым подходом для предотвращения конфликтов.obj

/questions/2408462/visual-studio-2010-and-2008-ne-mozhet-obrabatyivat-ishodnyie-fajlyi-s-odinakovyimi-imenami-v-raznyih-papkah/2408472#2408472

Имея в виду вышеизложенное, я могу уточнить, что "всякий раз, когда параметр MSBuild отправляется в CL.exe, он, кажется, нарушает или строго ограничивает /MP". /MP работает по одному вызову CL.exe за раз. Когда вы "отправляете параметр msbuild", на самом деле вы создаете несколько вызовов CL.exe с командной строкой, адаптированной к каждому исходному файлу. Они не могут быть упакованы. Вышеупомянутое связанное решение пытается обойти это путем адаптации командной строки к минимальному набору выходных каталогов.

Это должно решить вашу проблему, предотвращая клобберы при сохранении высокой степени пакетирования, если ваш проект не содержит 100 файлов с именами x.cpp в разных каталогах... обычно, по моему опыту, существует только несколько коллизий.obj в гораздо большем проекте.

Согласно MSDN, файлы должны быть скомпилированы, когда есть поток для их обработки, в то же время он не дает никаких гарантий относительно порядка, в котором файлы должны быть скомпилированы:

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

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

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

Это значение является меньшим из числа исходных файлов, которые вы указываете в командной строке

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

Возможно, вам удастся обойти это, если вы сгенерировали собственный файл make, в котором вы сможете одновременно обрабатывать несколько папок (или попытаться использовать MSBUILD.exe инструмент).

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

Я заметил, что перестройка моего последнего проекта MSVC занимает слишком много времени. В частности, ядра ЦП практически не используются, число процессов cl.exe в диспетчере задач сильно варьируется, а в окне "Вывод" отображаются исходные файлы, скомпилированные в виде пакетов. Где-то от одного до 16 компилируются за один раз, небольшая пауза, затем другой набор файлов и так далее. Для сравнения, в моих старых проектах процессор практически полностью используется, а в окне "Вывод" отображается непрерывный поток скомпилированных исходных файлов.

Теперь большая разница в моем новом проекте заключалась в том, чтобы лучше использовать пространства имен с соответствующей структурой каталогов, что означало, что некоторые классы имели одинаковые имена и вызывали конфликты из-за того, что разные файлы.obj направлялись в один и тот же каталог, что приводило к изменить имя файла объекта в C/C++ -> Выходные файлы.

Обновления в окне вывода также соответствуют структуре каталогов. Если есть пространство имен / каталог с одним исходным файлом внутри, тогда VS показывает только один файл, который компилируется за раз. Следующий каталог содержит 10 исходных файлов, и VS показывает, что все 10 компилируются одновременно.

Есть не так много решений. Либо избегайте классов с одинаковым именем и не изменяйте имя файла объекта, либо используйте обходной путь, опубликованный zeromus, он прекрасно работает. Мое время перестроения изменилось с 03:15 до 01:20, что довольно существенно и соответствует загрузке ЦП, которая составляет от 35% до 100% во время компиляции.

VS 2015, 2017 и 2019 все ведут себя таким образом, так что особых надежд на перемены нет.

Другие вопросы по тегам