如何为Windows上的一个线程保留核心?

时间:2011-03-17 23:57:13

标签: windows multicore

我正在开发一个非常时间敏感的应用程序,它会在检测到发生了更改时轮询共享内存区域。变化很少,但我需要尽量减少从变更到行动的时间。鉴于变化的频率很低,我认为CPU缓存变冷了。有没有办法为我的轮询线程保留一个核心,以便它不必与其他线程竞争缓存或CPU?

5 个答案:

答案 0 :(得分:6)

仅线程关联(SetThreadAffinityMask)是不够的。它不保留CPU核心,但它反过来,它将线程绑定到你指定的核心(这不是同一个东西!)。 / p>

通过约束CPU亲和性,您减少线程运行的可能性。如果具有更高优先级的另一个线程在同一个核心上运行,则在完成其他线程之前不会调度您的线程(这是Windows调度线程的方式)。 在不约束亲和力的情况下,您的线程有可能被迁移到另一个核心(最后一次作为该决策的度量运行)。线程迁移是不可取的,如果它经常发生并且在线程运行后(或者在它运行时)很快就会发生,但如果自上次调度后已经过了几十毫秒,则它是一种无害的,有益的事情(缓存将被覆盖然后无论如何)。

你可以通过给予它一个更高优先级的类来确保你的线程可以运行(不保证,但很可能)。如果你然后使用SetThreadAffinityMask,你有可能在大多数常见的桌面CPU(幸运的是通常是VIPT和PIPT)上缓存总是很热。对于TLB,你可能不那么幸运,但你无能为力。

高优先级线程的问题在于它会使其他线程饿死,因为调度已经实现,因此它首先提供更高优先级的类,并且只要不满足这些,下级类就会变为零。因此,在这种情况下的解决方案必须是阻止。否则,您可能会以不利的方式损害系统。

试试这个:

  • 创建信号量并与其他进程共享
  • 将优先级设置为THREAD_PRIORITY_TIME_CRITICAL
  • 阻止信号量
  • 在另一个进程中,在写入数据后,在信号量上调用SignalObjectAndWait,超时为1(甚至超时为零)
  • 如果您愿意,可以尝试将它们绑定到同一个核心

这将创建一个线程,它将是第一个(或第一个)获得CPU时间的线程,但它没有运行。 当写入器线程调用SignalObjectAndWait时,它会自动发出信号并阻塞(即使它等待“零时间”足以重新安排)。另一个线程将从信号量中唤醒并完成其工作。由于其高优先级,它不会被其他“正常”(即非实时)线程中断。它会一直占用CPU时间直到完成,然后再次阻塞信号量。此时,SignalObjectAndWait返回。

答案 1 :(得分:1)

使用任务管理器,您可以设置进程的“亲和力”。

您必须将时间要求严格的应用程序与核心4的亲和力,以及所有其他进程与核心1,2和3的亲和力进行设置。当然假设有四个核心。

答案 2 :(得分:0)

您可以在每个进程上调用SetProcessAffinityMask但是您的掩码只会排除“属于”您的进程的核心,并在您的进程上使用它将其设置为仅在此核心上运行(或者更好的是SetThreadAffinityMask只是在执行时间关键任务的线程上。

答案 3 :(得分:0)

  

鉴于变化的频率很低,我认为CPU缓存变冷了。

听起来很奇怪。

让我们假设你的轮询线程和写线程在不同的核心上。

轮询线程将读取共享内存地址,因此将缓存数据。该缓存行可能标记为独占。然后写线程最终写入;首先,它读取内存的缓存行(以便该行现在在两个内核上标记为共享)然后写入。写入会导致轮询线程CPU的高速缓存行被标记为无效。轮询线程然后再次阅读;如果在写入线程仍然具有缓存的数据时读取,则它将从第二个核心缓存中读取,使其缓存行无效并获取自身的所有权。这样做有很多总线流量开销。

另一个问题是写线程如果不经常写,几乎肯定会丢失具有共享内存地址的页面的TLB条目。重新计算物理地址是一个漫长而缓慢的过程。由于轮询线程经常轮询,可能该页面始终位于核心TLB中;从这个意义上讲,你可能会在延迟方面做得更好,让两个线程都在同一个核心上。 (虽然如果它们都是计算密集型的,它们可能会破坏性地干扰并且成本可能会高得多 - 我不知道,因为我不知道线程在做什么)。

你可以做的一件事是在写线程核心上使用超线程;如果你早知道你要写,那就去超线读取共享内存地址。这将在写入线程仍处于繁忙计算状态时加载TLB和缓存,从而为您提供并行性。

答案 4 :(得分:-1)

您正在寻找Win32函数SetThreadAffinityMask()