Skip to content

Commit

Permalink
Parallel build with CMake.
Browse files Browse the repository at this point in the history
  • Loading branch information
Holt59 committed Jul 13, 2024
1 parent e618ca5 commit ceef2d0
Showing 1 changed file with 4 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/tasks/modorganizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,12 @@ namespace mob::tasks {

// run cmake --build with default target
// TODO: handle rebuild by adding `--clean-first`
// TODO: have a way to specify the `--parallel` value - 16 is useful to build
// game_bethesda that has 15 games, so 15 projects
run_tool(cmake(cmake::build)
.root(source_path())
.arg("--parallel")
.arg("16")
.configuration(mob::config::relwithdebinfo));

// run cmake --install
Expand Down

4 comments on commit ceef2d0

@AnyOldName3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're using MSBuild or Ninja generators, they both default to using maximum parallelism (to a fault in the case of MSBuild, which will build as many .vcxprojes in parallel as you have cores, which will then each spawn as many cl.exe processes as you have cores, so if you're building a project with lots of subprojects with lots of files on a machine with lots of cores, you can end up with a ludicrous number of parallel jobs and overwhelm your machine). Anyway, my point is that if you're using a sensible CMake generator, this change won't be beneficial anywhere, but will be harmful on machines with more or fewer than sixteen hardware threads.

@Holt59
Copy link
Member Author

@Holt59 Holt59 commented on ceef2d0 Jul 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're using MSBuild or Ninja generators, they both default to using maximum parallelism (to a fault in the case of MSBuild, which will build as many .vcxprojes in parallel as you have cores, which will then each spawn as many cl.exe processes as you have cores, so if you're building a project with lots of subprojects with lots of files on a machine with lots of cores, you can end up with a ludicrous number of parallel jobs and overwhelm your machine). Anyway, my point is that if you're using a sensible CMake generator, this change won't be beneficial anywhere, but will be harmful on machines with more or fewer than sixteen hardware threads.

Not sure where your information comes from but unless I'm missing something, that's not true at all for VS generator:

  • Without --parallel, CMake will build each project sequentially, with --parallel, CMake will will build them in parallel. Typically in the case of Bethesda games where you have dependency like gamebryo > game or gamebryo > creation > game, games are built one after the other without --parallel but in paralle with --parallel.
  • /MP is not enabled by default for CMake (not sure if that was assumed by your comment or not).

On my machine, without --parallel or /MP, it takes 3m30s to build game_bethesda, adding --parallel 16 drops that to 52s and combining with /MP this drops to 26s (/MP without --parallel is about equivalent to --parallel without /MP).

If you want to reproduce (in case I'm missing) something:

  1. Clone https://github.com/ModOrganizer2/modorganizer-game_bethesda, branch dev/vcpkg
  2. cmake --preset vs2022-windows-standalone with Qt in your prefix path.
  3. cmake --build vsbuild --config RelWithDebInfo with or without --parallel. You cannot disable /MP as easily because it's part of MO2 common CMake configuration.

@AnyOldName3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought CMake Common set /MP for everything, so I was indeed taking it for granted that we'd have parallelism for multiple C++ files within the same project by default.

If you ask MSBuild or Visual Studio to build a whole solution file or a specific project within a solution file, it does build any other projects doing so would require in parallel by default (unless obviously they depend on each other so one has to finish before the next can start). I was under the impression that, cmake --build . --config RelWithDebInfo behaved the same way, and I thought this was something I'd tested at some point. I could just have been wrong, but I guess it's also possible that either CMake or Microsoft changed the default behaviour due to the core-count-squared number of cl.exe processes issue I mentioned.

Depending on project structure and machine specifics, it might be better to instead enable UseMultiToolTask and EnforceProcessCountAcrossBuilds, which would make MSBuild behave more like Ninja, i.e. defaulting to building multiple projects in parallel if there weren't enough files to keep the CPU busy within one project, and avoiding creating more jobs than cores. In my experience, if you've got one project with lots of files, it's slower than /MP on its own as there's a little more overhead, but if you've got multiple projects with lots of files, it's way faster than the combination of /MP and letting MSBuild build multiple projects in parallel. However, any type of project structure is faster with Ninja.

Anyhow, this has turned out more complicated than I'd expected. As it sounds like we're indeed using /MP, it's not the end of the world if the number of projects we're building in parallel is less than the number of cores, as those projects still have multiple files. It's just the thing where if we switch to Ninja later we'll want to get rid of this or make it pick the actual core count that's bugging me really.

@Holt59
Copy link
Member Author

@Holt59 Holt59 commented on ceef2d0 Jul 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought CMake Common set /MP for everything, so I was indeed taking it for granted that we'd have parallelism for multiple C++ files within the same project by default.

Yes, that's still the case.

Depending on project structure and machine specifics, it might be better to instead enable UseMultiToolTask and EnforceProcessCountAcrossBuilds, which would make MSBuild behave more like Ninja, i.e. defaulting to building multiple projects in parallel if there weren't enough files to keep the CPU busy within one project, and avoiding creating more jobs than cores. In my experience, if you've got one project with lots of files, it's slower than /MP on its own as there's a little more overhead, but if you've got multiple projects with lots of files, it's way faster than the combination of /MP and letting MSBuild build multiple projects in parallel. However, any type of project structure is faster with Ninja.

Anyhow, this has turned out more complicated than I'd expected. As it sounds like we're indeed using /MP, it's not the end of the world if the number of projects we're building in parallel is less than the number of cores, as those projects still have multiple files. It's just the thing where if we switch to Ninja later we'll want to get rid of this or make it pick the actual core count that's bugging me really.

I will check UseMultiToolTask and EnforceProcessCountAcrossBuilds but I do not think the --parallel flag will have such a negative impact. There are very few repositories with multiple projects, the two "biggest" ones would be game_bethesda and plugin_python. Maybe I could see about setting CMAKE_BUILD_PARALLEL_LEVEL in these two projects instead of global --parallel in mob.

Please sign in to comment.