Mar*_*som 25 multithreading operating-system multicore hyperthreading
我正在阅读新的英特尔凌动330的评论,他们注意到任务管理器显示4个核心 - 两个物理核心,另外两个由超线程模拟.
假设你有一个包含两个线程的程序.假设这些是在PC上进行任何工作的唯一线程,其他一切都是空闲的.操作系统将两个线程放在同一个核心上的概率是多少?这对程序吞吐量有很大影响.
如果答案不是0%,那么除了创建更多线程之外,是否还有其他缓解策略?
我希望Windows,Linux和Mac OS X会有不同的答案.
Linux具有非常复杂的线程调度程序,可以识别HT.其中一些策略包括:
被动负载平衡:如果物理CPU运行多个任务,则调度程序将尝试在第二个物理处理器上运行任何新任务.
主动负载均衡:如果有3个任务,当第二个物理处理器空闲时,一个物理cpu上有2个,另一个上有1个,调度程序将尝试将其中一个任务迁移到它.
它在尝试保持线程亲和性时执行此操作,因为当线程迁移到另一个物理处理器时,它将不得不从主内存重新填充所有级别的缓存,从而导致任务停顿.
所以回答你的问题(至少在Linux上); 在双核超线程机器上给出2个线程,每个线程将在其自己的物理核心上运行.
一个理智的操作系统会尝试在自己的内核上安排计算密集型任务,但是当您启动上下文切换时会出现问题.现代操作系统仍然倾向于在核心上安排工作,而这些核心在调度时没有工作,但这可能导致并行应用程序中的进程相当自由地从核心交换到核心.对于并行应用程序,您不希望这样,因为您丢失了该进程可能在其核心的高速缓存中使用的数据.人们使用处理器亲和力来控制它,但在Linux上,sched_affinity()的语义在发行版/内核/供应商等之间可能会有很大差异.
如果您使用的是Linux,则可以通过Portable Linux Processor Affinity Library(PLPA)轻松控制处理器关联.这就是OpenMPI内部使用的内容,以确保流程在多核和多串口系统中安排到自己的内核; 他们只是将模块拆分为独立项目.OpenMPI在洛斯阿拉莫斯的许多其他地方使用,所以这是经过良好测试的代码.我不确定Windows下的等价物是什么.
我一直在寻找关于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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过为两个线程提供处理器关联来确保它们被调度到相同的执行单元。这可以在 Windows 或 UNIX 中通过 API(以便程序可以请求它)或通过管理界面(以便管理员可以设置它)来完成。例如,在 WinXP 中,您可以使用任务管理器来限制进程可以在哪个逻辑处理器上执行。
否则,调度基本上是随机的,并且每个逻辑处理器的使用率预计为 25%。
归档时间: |
|
查看次数: |
11336 次 |
最近记录: |