27

我正在阅读对新 Intel Atom 330 的评论,他们注意到任务管理器显示 4 个内核 - 两个物理内核,再加上两个由超线程模拟的内核。

假设你有一个有两个线程的程序。还假设这些是唯一在 PC 上做任何工作的线程,其他一切都是空闲的。操作系统将两个线程放在同一个内核上的概率是多少?这对程序吞吐量有很大的影响。

如果答案不是 0%,除了创建更多线程之外,还有其他缓解策略吗?

我预计 Windows、Linux 和 Mac OS X 会有不同的答案。


使用sk 的答案作为谷歌素材,然后按照链接,我在 Windows 中找到了GetLogicalProcessorInformation函数。它谈到“共享资源的逻辑处理器。这种类型的资源共享的一个例子是超线程场景。” 这意味着jalf是正确的,但它不是一个确定的答案。

4

8 回答 8

8

Linux 有一个非常复杂的线程调度程序,它支持 HT。它的一些策略包括:

被动负载平衡:如果一个物理 CPU 正在运行多个任务,调度程序将尝试在第二个物理处理器上运行任何新任务。

主动负载平衡:如果有 3 个任务,一个物理 cpu 上 2 个,另一个物理 cpu 上 1 个,当第二个物理处理器空闲时,调度程序将尝试将其中一个任务迁移到它。

它在尝试保持线程亲和性的同时这样做,因为当线程迁移到另一个物理处理器时,它必须从主内存重新填充所有级别的缓存,从而导致任务停止。

所以回答你的问题(至少在 Linux 上);给定双核超线程机器上的 2 个线程,每个线程将在其自己的物理内核上运行。

于 2008-12-11T19:04:10.750 回答
5

一个健全的操作系统会尝试在它们自己的内核上安排计算密集型任务,但是当你开始上下文切换它们时就会出现问题。现代操作系统仍然倾向于在调度时没有工作的内核上调度事情,但这可能导致并行应用程序中的进程相当自由地从内核交换到内核。对于并行应用程序,您不希望这样,因为您丢失了该进程可能一直在其核心缓存中使用的数据。人们使用处理器亲和性来控制这一点,但在 Linux 上,sched_affinity() 的语义在发行版/内核/供应商等之间可能会有很大差异。

如果您使用的是 Linux,则可以使用Portable Linux Processor Affinity Library (PLPA)以便携式方式控制处理器关联。这是OpenMPI在内部使用的,以确保进程在多核和多插槽系统中被调度到它们自己的内核;他们刚刚将该模块分离为一个独立的项目。OpenMPI 在洛斯阿拉莫斯以及其他许多地方都使用,因此这是经过良好测试的代码。我不确定 Windows 下的等价物是什么。

于 2008-12-11T19:10:26.387 回答
5

我一直在寻找有关 Windows 上的线程调度的一些答案,并且有一些经验信息,我将在此处发布,以供将来可能偶然发现这篇文章的任何人使用。

我编写了一个启动两个线程的简单 C# 程序。在我的四核 Windows 7 机器上,我看到了一些令人惊讶的结果。

当我没有强制关联时,Windows 将两个线程的工作负载分散到所有四个内核上。有两行代码被注释掉 - 一行将线程绑定到 CPU,另一行建议使用理想的 CPU。该建议似乎没有效果,但设置线程亲和性确实会导致 Windows 在各自的内核上运行每个线程。

要获得最佳结果,请使用 .NET Framework 4.0 客户端附带的免费提供的编译器 csc.exe 编译此代码,并在具有多核的计算机上运行它。注释掉处理器关联行后,任务管理器显示线程分布在所有四个内核上,每个内核运行率约为 50%。设置关联性后,两个线程以 100% 的速度将两个核心最大化,而其他两个核心处于空闲状态(这是我在运行此测试之前所期望看到的)。

编辑:我最初发现这两种配置的性能存在一些差异。但是,我无法复制它们,所以我编辑了这篇文章以反映这一点。我仍然发现线程亲和力很有趣,因为它不是我所期望的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class Program
{
    [DllImport("kernel32")]
    static extern int GetCurrentThreadId();

    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => ThreadFunc(1));
        Task task2 = Task.Factory.StartNew(() => ThreadFunc(2));
        Stopwatch time = Stopwatch.StartNew();
        Task.WaitAll(task1, task2);
        Console.WriteLine(time.Elapsed);
    }

    static void ThreadFunc(int cpu)
    {
        int cur = GetCurrentThreadId();
        var me = Process.GetCurrentProcess().Threads.Cast<ProcessThread>().Where(t => t.Id == cur).Single();
        //me.ProcessorAffinity = (IntPtr)cpu;     //using this line of code binds a thread to each core
        //me.IdealProcessor = cpu;                //seems to have no effect

        //do some CPU / memory bound work
        List<int> ls = new List<int>();
        ls.Add(10);
        for (int j = 1; j != 30000; ++j)
        {
            ls.Add((int)ls.Average());
        }
    }
}
于 2010-07-28T22:22:59.340 回答
3

这是一个非常好的和相关的问题。众所周知,超线程内核并不是真正的 CPU/内核。相反,它是一个虚拟 CPU/核心(从现在开始我会说核心)。从 Windows XP 开始,Windows CPU 调度程序应该能够区分超线程(虚拟)内核和真实内核。您可能会想象,在这个完美的世界中,它“恰到好处”地处理它们,这不是问题。你会错的。

Microsoft 自己的优化 Windows 2008 BizTalk 服务器的建议建议禁用超线程。这表明,对我来说,超线程内核的处理并不完美,有时线程会在超线程内核上获得时间片并遭受惩罚(真实内核性能的一小部分,10% 我' d 猜测,微软猜测 20-30%)。

Microsoft 文章参考,他们建议禁用超线程以提高服务器效率:http: //msdn.microsoft.com/en-us/library/cc615012 (BTS.10).aspx

这是 BIOS 更新后的第二个建议,这就是他们认为的重要性。他们说:

来自微软:

"在 BizTalk Server 和 SQL Server 计算机上禁用超线程

为BizTalk Server 计算机关闭超线程至关重要。这是 BIOS 设置,通常可以在 BIOS 设置的处理器设置中找到。超线程使服务器看起来比实际拥有更多的处理器/处理器内核;然而,超线程处理器通常提供物理处理器/处理器内核的 20% 到 30% 的性能。当 BizTalk Server 统计处理器数量以调整其自调整算法时;超线程处理器会导致这些调整出现偏差,这对整体性能不利。"

现在,他们确实说这是因为它抛弃了自调整算法,但接着又提到了争用问题(这表明这是一个更大的调度问题,至少对我来说是这样)。随意阅读,但我认为它说明了一切。超线程在使用单 CPU 系统时是一个好主意,但现在只是一个复杂的问题,可能会损害这个多核世界的性能。

除了完全禁用超线程之外,您还可以使用 Process Lasso(免费)等程序为关键进程设置默认 CPU 关联性,以便它们的线程永远不会被分配给虚拟 CPU。

所以....仍然不完美。事实上,它可能永远不会完美,因为操作系统不知道哪些线程最好放在这些较慢的虚拟内核上。这可能就是问题所在,也是微软建议在服务器环境中禁用超线程的原因。

还要记住,即使没有超线程,也会出现“核心抖动”的问题。如果您可以将线程保留在单个内核上,那是一件好事,因为它可以减少内核更改的损失。

于 2010-11-26T03:17:04.320 回答
3

操作系统不会使用尽可能多的物理内核的概率基本上是 0%。你的操作系统并不愚蠢。它的工作是安排一切,它完全知道它有哪些可用的内核。如果它看到两个 CPU 密集型线程,它将确保它们在两个物理内核上运行。

编辑 只是为了详细说明一下,对于高性能的东西,一旦你进入 MPI 或其他严肃的并行化框架,你肯定想要控制每个内核上运行的内容。

操作系统将尽最大努力尝试利用所有内核,但它没有您所做的长期信息,即“该线程将运行很长时间”或“我们'将有这么多线程并行执行“。所以它不能做出完美的决定,这意味着你的线程会不时被分配到一个新的核心,这意味着你会遇到缓存未命中等类似情况,这会花费一些时间。对于大多数用途来说,它已经足够好了,您甚至不会注意到性能差异。如果这很重要,它还可以与系统的其他部分配合使用。(在某人的桌面系统上,这可能相当重要。在一个有几千个 CPU 专用于这个任务的网格中,你并不是特别想玩得好,

因此,对于大型 HPC 的东西,是的,您会希望每个线程都保持在一个核心上,并且是固定的。但是对于大多数较小的任务,这并不重要,您可以信任操作系统的调度程序。

于 2008-12-11T18:31:34.687 回答
2

您可以通过为它们提供处理器亲和性来确保为相同的执行单元安排两个线程。这可以在 windows 或 unix 中通过 API(因此程序可以请求它)或通过管理界面(因此管理员可以设置它)来完成。例如,在 WinXP 中,您可以使用任务管理器来限制进程可以在哪些逻辑处理器上执行。

否则,调度将基本上是随机的,您可以预期每个逻辑处理器上的使用率为 25%。

于 2008-12-11T18:14:01.990 回答
1

我不知道其他平台,但就英特尔而言,他们在英特尔软件网络上发布了很多关于线程的信息。他们还有一个免费的时事通讯(英特尔软件调度),您可以通过电子邮件订阅,并且最近有很多这样的文章。

于 2008-12-11T18:32:32.483 回答
0

操作系统将 2 个活动线程分派到同一个内核的机会为零,除非线程绑定到特定内核(线程关联)。

这背后的原因主要与硬件有关:

  • 操作系统(和 CPU)希望使用尽可能少的功率,以便尽可能高效地运行任务,以便尽快进入低功耗状态。
  • 在同一个核心上运行所有东西会导致它更快地升温。在病态条件下,处理器可能会过热并降低其时钟以冷却。过热还会导致 CPU 风扇转得更快(想想笔记本电脑)并产生更多噪音。
  • 系统永远不会真正空闲。ISR 和 DPC 每毫秒运行一次(在大多数现代操作系统上)。
  • 在 99.99% 的工作负载中,由于线程从内核跳到内核导致的性能下降可以忽略不计。
  • 在所有现代处理器中,最后一级缓存是共享的,因此切换内核并不是那么糟糕。
  • 对于多插槽系统 (Numa),操作系统将最大限度地减少从插槽到插槽的跳跃,以便进程保持“靠近”其内存控制器。在优化此类系统(数十/数百个内核)时,这是一个复杂的领域。

顺便说一句,操作系统知道 CPU 拓扑的方式是通过 ACPI - BIOS 提供的接口。

总而言之,这一切都归结为系统电源考虑因素(电池寿命、电费、冷却解决方案的噪音)。

于 2014-01-06T22:39:49.740 回答