Skip to content

Do topology perms with threads#4270

Draft
chrisrichardson wants to merge 12 commits into
mainfrom
chris/threaded-topology-permutation
Draft

Do topology perms with threads#4270
chrisrichardson wants to merge 12 commits into
mainfrom
chris/threaded-topology-permutation

Conversation

@chrisrichardson

@chrisrichardson chrisrichardson commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Uses std::jthread to run topology permutation computation in parallel, enabling a speedup.
Also, moves the dolfinx::MPI::local_range computation to dolfinx::common::local_range since it is now being used for other things than MPI.

See #1250

@chrisrichardson chrisrichardson added performance housekeeping Tidying and style improvements labels Jul 7, 2026
…rflow in demos

Adds a new step "Configure matplotlib for CI" that creates a matplotlib
configuration file with `axes.unicode_minus: False` to work around FreeType
"raster overflow" crashes (error 0x62) when rendering Unicode minus signs in
plot labels for negative tick labels and log-scale exponents.

This resolves test failures in demo_lagrange-variants.py and demo_tnt-elements.py
that were caused by FT_Render_Glyph failures in the Spack/Ubuntu environment.
Comment on lines +170 to +173
int num_threads
= std::max(1, static_cast<int>(std::thread::hardware_concurrency()));
spdlog::debug("Using {} threads for face permutation computation",
num_threads);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I thought we always wanted this to be specified by the user as a input argument. That is what we have done for all other threaded functionality so far.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I know. Bear in mind this is still draft. I am not super convinced about passing num_threads around everywhere.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something we should discuss in a developer meeting :)

@garth-wells garth-wells Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Passing the number of threads is the right thing to do. There is no other good option.

@jhale jhale Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Had a quick chat about this with @chrisrichardson today and we do see a path forwards on this; use an explicit std::optional for num_threads and then fall back on an 'ambient' global. The ambient global could also be overridden using an RAII-scope guard.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The low-level functions should just have a num threads arg. It's clear, simple and explicit, and forces the caller to recognise that the function supports threads.

At a higher level we can work through options.

std::vector<std::bitset<BITSETSIZE>> edge_perm(num_cells, 0);
std::vector<std::int64_t> cell_vertices, vertices;
for (int c = 0; c < c_to_v->num_nodes(); ++c)
std::vector<std::jthread> threads(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment here.

@michalhabera michalhabera left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Few comments to consider:

  1. Don't default use std::thread::hardware_concurrency(). User runs with outer mpirun -n 2 and this oversubscribes badly.
  2. We need to stick to the same policy of meaning for num_threads in the API. It could either mean: a) total number of execution lanes including caller, or b) number of worker threads to spawn, besides the caller.
    For a), num_threads=0 is invalid, and num_threads=1 means serial, with no spawning. For b), num_threads=0 means serial, and num_threads=1 means the caller does its job (maybe waits?), and there is one thread spawned. I would strongly suggest to stick with a).
    Your PR does neither, since with num_threads=1 the caller idles for one spawned worker. That needs fixing. It is the point @schnellerhase was making in the other PR that introduced threading.
  3. Passing num_threads in the API is good, I agree, as long as this is mostly for few low-level interfaces, and we never allow calling a foo(num_threads=8) inside parallel region of a higher level call, i.e. a nesting. That would need to pass some richer context around.
  4. If num_threads pollutes the interfaces too much, for convenience, we can maybe consider it to have a non-zero, user changeable default value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

housekeeping Tidying and style improvements performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants