Skip to content

pthread: pin threads to the allowed CPUs, spread over NUMA nodes - #8

Open
maleadt wants to merge 1 commit into
tb/pthread-static-schedulefrom
tb/pthread-numa-affinity
Open

maleadt wants to merge 1 commit into
tb/pthread-static-schedulefrom
tb/pthread-numa-affinity

Conversation

@maleadt

@maleadt maleadt commented Oct 26, 2026

Copy link
Copy Markdown
Member

Stacked on #7.

POCL_AFFINITY=1 pinned thread i to CPU i. That causes three problems:

  • It ignores the process's CPUs. Under taskset -c 32-63 the threads ran on CPUs 0 and up anyway, and in a cpuset without CPU i pinning fails.
  • It packs subdevices onto one node. A subdevice uses the first N threads, which land on the first N CPUs, typically all on one NUMA node. On 2x EPYC 7502, a 32-thread subdevice ran SAXPY at 123 GB/s, the bandwidth of a single node.
  • It can pin the wrong thread. It went through td->thread, which the creating thread may not have set yet.

This PR instead pins the threads to the CPUs that the process's threads may run on, i.e. the union of their affinity masks. That respects taskset and cpusets, and includes the CPUs of applications that pin their own threads before using PoCL. The first threads get a core of their own, round-robin over the NUMA nodes, so any number of first threads is spread evenly. The 32-thread subdevice now reaches 158 GB/s, the same as OpenMP threads spread over both nodes.

With the static schedule, threads that run out of work-groups now take them from threads on the same NUMA node first.

Pinning stays opt-in, since PoCL can't know whether other processes or runtimes use the same CPUs.

With POCL_AFFINITY=1, thread i was pinned to CPU i. That ignores the
CPUs the process may run on: under taskset -c 32-63, the threads ran on
CPUs 0 and up anyway, and in a cpuset without CPU i, pinning fails. It
also puts a subdevice of the first N threads on the first N CPUs, which
are typically all on one NUMA node: on 2x EPYC 7502, a 32-thread
subdevice ran SAXPY at 123 GB/s, the bandwidth of a single node. And it
pinned through td->thread, which the creating thread may not have set
yet.

Pin the threads to the CPUs that the process's threads may run on (the
union of their affinity masks, which includes the CPUs of applications
that pin their own threads), one per core first, round-robin over the
NUMA nodes, so that any number of first threads is spread evenly. The
32-thread subdevice now reaches 158 GB/s, like OpenMP threads spread
over both nodes. With the static schedule, threads that run out of
work-groups now also take them from threads on the same node first.
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