我在某处听说过 Thread Affinity 和 Thread Affinity Executor。但至少在java中我找不到合适的参考。有人可以向我解释一下这是怎么回事吗?
4 回答
有两个问题。首先,线程最好与某个 CPU(核心)具有亲和力,以充分利用其 CPU 本地缓存。这必须由操作系统处理。线程的这种CPU 亲和性通常也称为“线程亲和性”。在 Java 的情况下,没有标准的 API 来控制它。但是正如其他答案所提到的,有 3rd 方库。
其次,在 Java 中观察到,在典型的程序中对象是线程仿射的,即通常大多数时间只由一个线程使用。因此,JVM 优化器的任务是确保与一个线程相关的对象在内存中彼此靠近放置以适应一个 CPU 的高速缓存,但将与不同线程相关的对象放置在彼此之间不要太近以避免它们共享一个缓存行,否则两个 CPU/核心必须经常同步它们。
理想的情况是,一个 CPU 可以独立于另一个 CPU 处理放置在不相关内存区域中的其他对象来处理某些对象。
考虑 Java 对象的线程亲和性的优化的实际示例是
- 线程局部分配缓冲区 (TLAB)
使用 TLAB,每个对象的生命周期都在专用于创建它的线程的内存区域中开始。根据分代垃圾收集器背后的主要假设(“所有对象中的大多数将在年轻时死去”),大多数对象将在这样的线程本地缓冲区中度过它们的整个生命周期。
- 偏向锁定
使用偏向锁定,JVM 将在乐观的假设下执行锁定操作,即对象将仅由同一线程锁定,仅当此假设不成立时才切换到更昂贵的锁定实现。
- @竞争
为了解决另一端,即已知可被多个线程访问的字段,HotSpot/OpenJDK 有一个注释(目前不属于公共 API 的一部分)来标记它们,以指示 JVM 将这些数据从另一端移开,可能未共享的数据。
Java Thread Affinity 1.4 版库试图实现两全其美,允许您为关键线程保留逻辑线程,并为性能最敏感的线程保留整个内核。不太关键的线程仍将利用超线程的优势运行。例如下面的代码片段
AffinityLock al = AffinityLock.acquireLock();
try {
// find a cpu on a different socket, otherwise a different core.
AffinityLock readerLock = al.acquireLock(DIFFERENT_SOCKET, DIFFERENT_CORE);
new Thread(new SleepRunnable(readerLock, false), "reader").start();
// find a cpu on the same core, or the same socket, or any free cpu.
AffinityLock writerLock = readerLock.acquireLock(SAME_CORE, SAME_SOCKET, ANY);
new Thread(new SleepRunnable(writerLock, false), "writer").start();
Thread.sleep(200);
} finally {
al.release();
}
// allocate a whole core to the engine so it doesn't have to compete for resources.
al = AffinityLock.acquireCore(false);
new Thread(new SleepRunnable(al, true), "engine").start();
Thread.sleep(200);
System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks());
线程亲和性(或进程亲和性)描述了允许线程/进程在哪些处理器内核上运行。通常,此设置等于系统中的(逻辑)CPU,几乎没有理由更改此设置,因为操作系统具有在可用处理器中调度任务的最佳可能性。
请参阅http://msdn.microsoft.com/en-us/library/windows/desktop/ms683213(v=vs.85).aspx了解它在 Windows 中的工作原理。我不知道 java 是否提供 API 来设置这些。