为什么单线程进程会在多个处理器/核心上执行?

12

假设我运行像下面这样的简单单线程进程:

public class SirCountALot {
    public static void main(String[] args) {
        int count = 0;
        while (true) {
            count++;
        }
    }
}

(这是Java,因为这是我熟悉的语言,但我认为这并不重要)

我有一台i7处理器(4个核心或8个逻辑核心),并且我正在运行Windows 7 64位系统,所以我启动了Sysinternals Process Explorer来查看CPU使用情况,正如预期的那样,我看到它正在使用大约20%的所有可用CPU。

显示所有核心使用20%CPU的图表

但是,当我切换选项以显示每个CPU的1个图表时,我看到CPU使用情况分散在所有核心上,而不是使用4个“核心”中的1个,如下所示:

显示每个核心上不稳定的CPU使用率,总共约为20%

而我所期望的是一个核心被占满,但只有当我将进程的亲和力设置为单个核心时才会发生这种情况。

显示最近的大部分CPU使用率仅限于第一个核心

为什么工作负载分布在不同的核心上?将工作负载分配到多个核心会不会影响缓存或产生其他性能损失?

是为了防止一个核心过热吗?还是有其他更深层次的原因?

编辑:我知道操作系统负责调度,但我想知道它为什么“介意”。从天真的角度来看,将(大多数*)单线程进程粘附到1个核心上是更简单和更有效的方法,不是吗?

*我说大多数单线程是因为这里有多个线程,但只有其中的2个在做任何事情:

显示Eclipse中线程数的截图 显示Process Explorer进程属性中线程数的截图


2
小细节:说这是单线程进程是不正确的。JVM 内部会为了诸如 finalizers、垃圾回收等维护目的而生成多个线程。很可能为了让每个线程完成真正的工作,JVM 线程被映射到真实的硬件线程上,这也可能解释了分布情况。 - Sanjay T. Sharma
1
我猜Caspar指的是非守护线程。 - Santosh
@SanjayT.Sharma 是的,我简化了一些内容,可能应该给出一个非托管语言的示例程序;但是正如我所说,我强烈怀疑这不是JVM在做这件事情(如果它正在将JVM-> HW线程映射,并且这是原因,为什么映射不断变化?) - Caspar
@Santosh 是的,确切地说,我指的是那些不会99%时间处于空闲状态的线程。 - Caspar
2个回答

20

操作系统负责调度。它可以自由地停止一个线程,然后在另一个 CPU 上重新开始。即使机器没有其他任务,它也会这样做。

进程在 CPU 之间移动,因为操作系统不会假定每次都有理由在同一 CPU 上继续运行线程。

因此,我编写了一个库来锁定线程到一个 CPU 上,以便它不会移动并且不会被其他线程中断。这降低了延迟并提高了吞吐量,但会占用一个 CPU。此方法适用于 Linux,也许您可以为 Windows 进行适配。 https://github.com/peter-lawrey/Java-Thread-Affinity/wiki/Getting-started


我想我没有表达清楚;我知道操作系统进行调度,你可以在第二张图中看到我已经设置了进程的亲和性,使其仅使用第一个核心。我想知道的是,为什么操作系统会将单个“活动”线程调度到所有可用的核心上? - Caspar
相反的问题是,为什么要将线程重新分配给同一个CPU,而不是只将其分配给下一个空闲的CPU(这就是它所做的)。使用轮询法可以很好地处理无论有多少个CPU正在忙碌。每次分配到同一个CPU可能会使一个CPU非常繁忙(有两个线程在其上运行),而其他CPU则处于空闲状态。 - Peter Lawrey
并不是完全离题,而是“旁题”问题:你真正需要这样的东西的使用案例是什么?金融交易应用程序?你需要它做什么,还是只是一个私人案例研究项目? - Fabian Barney
调度器旨在分摊工作负载,这对于有比CPU更多活动线程的机器来说是有意义的。如果您有更多核心而不是关键线程,则使用已识别为关键的那些线程的亲和力是一个好理由(这就是我编写该库的原因)。 据我所知,您无法停止线程被中断(但可以减少它)。 - Peter Lawrey
1
尽可能将一个持续准备运行的线程保持在同一核心上有很好的理由——该线程所需的所有内容都在运行该线程的核心上。这包括L1和L2缓存、分支预测缓冲区、TLB等。更改核心意味着必须重新填充所有这些缓存。(如果线程总是准备好运行,这只适用于此。如果核心执行其他操作,因为该线程无法继续运行,则即使将线程重新分配回同一核心,缓存也会变得冷。) - David Schwartz
显示剩余4条评论

1
我也希望CPU和操作系统能够有意地这样做,以尝试在CPU芯片上分散热负载...因此它会将(唯一/单个)线程从核心旋转到另一个核心。可以承认这可能是反对过于努力地抵制这种情况的一个论点(尤其是实际上,你经常会看到通过简单调整/改进应用程序本身来获得更好的改进)。

有趣。你知道Windows/Linux肯定这样做,还是只是个猜想?(另外,欢迎来到stackoverflow :) - Leeor
我已经清楚地看到这种情况在OSX和Windows上发生。我希望Linux也是一样的,但从未特别尝试验证它。 - Camlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接