proc_bind 各策略的 place 计算逻辑、affinity_thr 的内存管理,gomp_simple_barrier_reinit 如何保证同步
- Micro Topics
- 2025-05-29
- 342 Views
- 0 Comments
- 863 Words
很好!你提的这三个点都直指 GOMP_parallel 线程调度核心机制。我们逐一详细剖析这三项:
🔢 一、proc_bind 各策略下的 place 计算逻辑
这个逻辑体现在你贴出的代码中 switch (bind) 语句块下的 线程 affinity 计算部分,其作用是:为第 i 个线程计算它应绑定的 place_partition_off 和 place_partition_len。
proc_bind(master)
-
策略说明:所有线程都绑定到主线程所在的 place。
-
计算逻辑:不变更
p,k,s等变量。 -
效果:所有线程执行在同一个核域。
proc_bind(close)
-
策略说明:将线程集中绑定到主线程相邻的核域中。
-
计算逻辑(T ≤ P):
-
顺序分配 place。
-
每
s个线程共用一个 place。
-
-
变量更新:
if (k == s) { ++p; ... k = 1; } else ++k; -
效果:T 个线程被“打包”成组贴着主线程的核域依次扩展。
proc_bind(spread)
-
策略说明:尽量分散线程,使它们跨越更多核域。
-
计算逻辑分两种情况:
1. T ≤ P(线程数少于 place 数)
if (p < rest)
p += s + 1;
else
p += s;
-
每个 place 最多绑定一个线程。
-
place 分配拉开距离,防止集中在一个核域。
2. T > P(线程数多于 place 数)
if (k == s) {
++p;
...
k = 1;
} else
++k;
-
类似于
close,但粒度为 1。 -
平均地将多个线程“摊开”绑定在每个 place 上。
🧠 二、affinity_thr 的作用与内存管理
affinity_thr 是什么?
这是一个临时线程指针数组,用于将线程按 place 分类(partition),从而在复用线程池中挑选“刚好处于某个 place 的线程”。
affinity_thr = gomp_malloc(...) 或 gomp_alloca(...)
-
它的大小为当前 place_partition 的长度(比如 64 个核域就有 64 个 slot)。
-
每个 slot 是一个链表头,链表中的线程都是已经绑定在该 place 的旧线程(即线程池中待复用的线程)。
如何管理它的生命周期?
if (team->prev_ts.place_partition_len > 64)
free (affinity_thr);
-
如果分配用的是
gomp_malloc(),则手动释放; -
如果使用的是
gomp_alloca(),即放在栈上,不需释放; -
为了节省内存开销,小于等于 64 的情形不使用堆分配。
⛓ 三、gomp_simple_barrier_reinit 如何保障线程同步?
这是一个轻量级 barrier,用于保障线程在被复用或创建之后,全部准备好后才能一起进入工作区间(即进入 fn())。
✅ 调用位置
gomp_simple_barrier_reinit(&pool->threads_dock, nthreads);
它表示:“这次 barrier 等待 nthreads 个线程到达后才释放”。
✅ 功能逻辑(简化理解):
struct gomp_barrier {
atomic_uint count;
unsigned int threshold;
};
初始化 / Reinit:
count = 0;
threshold = nthreads;
每个线程执行:
if (atomic_fetch_add(&count, 1) + 1 == threshold)
// 最后一个线程来了,释放所有线程
else
// 阻塞等待最后一个线程
✅ 特点:
-
不使用重型锁,只用原子变量 + 内存屏障,开销非常小。
-
是“复用线程池”的关键组件 —— 所有旧线程需要靠这个同步才能一起开始新任务。
🔚 总结
| 关键机制 | 核心作用 | 实现重点 |
|---|---|---|
proc_bind 策略 |
控制线程分布在 CPU 上的拓扑 | 按策略计算每线程绑定的 place |
affinity_thr |
临时结构,复用线程时按核域分类 | 栈/堆分配,使用后可释放 |
gomp_simple_barrier_reinit |
线程同步起点屏障 | 轻量级 barrier,等待所有线程 ready 后统一起跑 |
如果你想继续看 gomp_team_start 中线程实际启动的位置、或者进入 gomp_thread_start 看工作线程是如何参与 barrier 的,也可以继续深入!
