例如,在X86中,两个CPU内核正在运行不同的软件线程。
同时,这两个线程需要同时在其CPU内核上运行。
有没有办法同步这2个CPU内核/线程,或类似的方法使它们在同一时间(在指令级别)开始同时运行(几乎)?
答案 0 :(得分:5)
使用共享变量在两个线程之间传递基于rdtsc
的截止日期。例如,设置一个截止日期,例如说当前的rdtsc
值加上10,000。
然后让两个线程都在rdtsc
上旋转,直到当前rdtsc
值和阈值之间的 gap 小于阈值T
(T = 100应该可以)。最后,使用最终间隔值(即,截止日期rdtsc
值减去最后读取的rdtsc
值)跳入相关的相加指令序列,以使相加指令的数量等于间隔
最后一步弥补了以下事实:每个芯片相对于其rdtsc
自旋环路通常不会“同相”。例如,假设rdtsc
读数有30个周期的背对背吞吐量,则一个芯片可能会获得890、920、950等的读数,而另一个芯片可能会读取880、910、940的读数,因此会有10如果仅使用rdtsc
,则为20个循环错误。使用加法滑动补偿,如果最后期限为1,000,且阈值为100,则第一个线程将在rdtsc == 920
处触发并执行80次添加,而第二个线程将在rdtsc == 910
处触发并执行90次添加。原则上,两个内核都将大致同步。
一些注意事项:
rdtsc
的频率-如果不是这种情况,则在计算跳转到添加幻灯片的位置时,您必须基于标称值至真实的频率比来应用补偿因子。T
足够大,以使间隙始终为正,因此比背对背rdtsc
的延迟要大一些,但不要太大,以免增加发生类似事件的机会在添加幻灯片期间中断。rdtsc
或rdtscp
来检查“同步”的有效性,并查看记录值在线程之间的接近程度。一个完全不同的选择是使用Intel TSX:事务扩展。为两个要进行协调的线程进行组织,以同时读取事务区域内的共享线然后旋转,并让第三个线程写入共享线。这将导致两个等待线程中止。根据内核间的拓扑,两个等待线程可能会收到无效消息,因此随后的TSX几乎同时终止。从中止处理程序中调用要同步运行的代码。
答案 1 :(得分:3)
根据您对“(几乎同时)”的定义,从微体系结构上来讲,这是一个非常棘手的问题。
如果您担心计时到周期,即使“运行”的定义也不够明确。您的意思是从前端到无序后端的问题吗?执行? (派遣到执行单位?还是无需重播就可以成功完成执行?)还是退休?
我倾向于使用Execute 1 ,因为那是像rdtsc
这样的指令对时间戳计数器进行采样的时候。这是您可以实际记录其时间,然后稍后进行比较的一个。
脚注1:在正确的道路上,不要误以为是,除非您对没有退休的处决还满意。
但是,当您关心的指令执行时,如果两个内核具有不同的ROB / RS状态,则它们将不会以锁步方式继续。 (有序的x86-64 CPU很少,例如一些Silverver之前的Atom和Xeon Phi早期的产品:Knight's Corner。今天的x86-64 CPU都是乱序的,并且在低功耗Silvermont之外。 -家庭积极,因此使用大型ROB +调度程序。)
我还没有使用过它,但是x86 asm monitor
/ mwait
可以让两个CPU监视并等待对给定内存位置的写操作。我不知道唤醒如何同步。我猜想睡眠越深,延迟的变化就越小。
总是可以在写之前将中断从唤醒中唤醒。除非您禁用中断,否则您将不可能100%地做到这一点。希望您只需要在合理的成功机会下实现它,并能够在事后告诉您是否实现了它。
(在最近的低功耗Intel CPU(Tremont)上,这些可用的用户空间可用版本是:umonitor
/ umwait
。但是在内核中,您可能只使用{{ 1}} / monitor
)
如果mwait
/ umonitor
可用,则意味着您具有 WAITPKG CPU功能,该功能还包括tpause
:与umwait
类似,但请稍候直到给定的TSC时间戳。
在现代x86 CPU上,TSC 通过硬件在所有内核之间同步,因此对多个内核使用相同的唤醒时间就变得很简单。
否则,您可以在pause
截止日期之前等待,并且在Skylake上最糟糕的情况下可能会花费约25个周期。
rdtsc
在Skylake(https://agner.org/optimize/)上每25个周期有一个吞吐量,因此您希望每个线程平均晚12.5个周期,离开旋转等待循环+ -12.5。我假设两个线程的branch-mispredict成本是相同的。这些是核心时钟周期,而不是rdtsc计数的参考周期。 RDTSC通常接近最大非涡轮时钟。有关来自C的RDTSC的更多信息,请参见How to get the CPU cycle count in x86_64 from C++?。
请参见 How much delay is generated by this assembly code in linux,以获取在rdtsc
上旋转的asm函数,以等待截止日期。您可以很容易地用C编写此代码。
在多核Xeon上,每个核可以独立更改频率,您需要将CPU频率固定为某个值,最大非涡轮增压可能是个不错的选择。否则,如果内核使用不同的时钟速度,它们显然会立即不同步。
在台式机上,您可能还是想这样做,以防万一暂停时钟来更改CPU频率会丢掉这些东西。
分支错误预测,缓存未命中甚至ROB / RS的初始状态的任何差异都可能导致严重的不同步。
更重要的是,与在已经运行的任务中再运行一条指令相比,中断是巨大的并且花费很长的时间。甚至可能导致调度程序将上下文切换到另一个线程。或为该任务进行CPU迁移,显然会花费很多周期。