Skip to content

perf(app): spread pinned workers across physical cores before SMT siblings - #376

Merged
gg582 merged 1 commit into
devfrom
perf/smt-aware-affinity
Oct 14, 2026
Merged

gg582 merged 1 commit into
devfrom
perf/smt-aware-affinity

Conversation

@gg582

@gg582 gg582 commented Oct 14, 2026

Copy link
Copy Markdown
Member

Problem

cwist_app_pin_worker() pins worker i to the (i mod n)-th allowed CPU in ascending order. That only spreads workers across physical cores when the kernel numbers SMT siblings far apart (0,N). Many hosts number them next to each other (0,1, 2,3, …), including many cloud VMs and some Intel and ARM boxes. On those hosts, CWIST_WORKERS=4 puts the four workers on the two hyperthreads of two cores while the remaining cores sit idle.

Follow-up to the #319 investigation. The benchmark box there numbers siblings 0,6, so it never showed this.

Change

src/sys/app/worker_affinity.h:

  • Each allowed CPU gets a rank: the number of its allowed SMT siblings with a lower ID. Siblings come from topology/core_cpus_list, falling back to thread_siblings_list on older kernels.
  • CPUs are picked in (rank, ID) order, so every physical core gets one worker before any core gets a second.
  • Unchanged cases: no SMT, 0,N numbering, no sysfs (some containers), or an unreadable or malformed topology file. In all of these the order stays the old ascending one, so the change is a strict improvement or a no-op.
  • The inherited mask is still respected. A core with only one allowed thread counts as rank 0.
Siblings Before (workers 0..7) After
0-1,2-3,4-5,6-7 0 1 2 3 4 5 6 7 0 2 4 6 1 3 5 7
0,4 1,5 2,6 3,7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
4-way 0-3,4-7 0 1 2 3 4 5 6 7 0 4 1 5 2 6 3 7

There's no new API or env knob, and no public header changes.

Tests

tests/test_worker_affinity.c points the reader at a fake sysfs root and checks:

  • adjacent, split and 4-way orders (the last one through thread_siblings_list only);
  • a partial mask;
  • malformed files, which fall back to ascending order.

The existing mock and real-child cases are unchanged; they run with no topology exposed.

On this host (12 CPUs, siblings 0,6) the real order is unchanged: 0 1 … 11. taskset -c 0,1,6,7 gives 0 1 6 7.

Local runs of the test are clean, including ASan/UBSan, and clang-format reports nothing.

…lings

cwist_app_pin_worker took the (index % n)-th allowed CPU in ascending
order. On hosts that number SMT siblings next to each other (0,1 / 2,3,
common on many VMs and older Intel/ARM boxes), four workers landed on
the two threads of two cores while other cores sat idle.

Rank each allowed CPU by the number of allowed siblings with a lower ID
(core_cpus_list, falling back to thread_siblings_list) and pick by
(rank, ID), so every physical core gets one worker before any gets two.
Without SMT, without sysfs, or with an unreadable topology file the
order is the old plain ascending one; split numbering (0,N) is also
unchanged.

The test points the reader at a fake sysfs root and checks adjacent,
split and 4-way SMT orders, a partial mask, and malformed files.
@gg582
gg582 merged commit aec6829 into dev Oct 14, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant