想象一下,我希望有一个主线程和一个辅助线程作为同一物理内核上的两个超线程运行(可能通过强制它们的亲和力来大致确保这一点).
主线程将做重要的高IPC,CPU绑定工作.除了定期更新主线程将定期读取的共享时间戳值之外,辅助线程应该不执行任何操作.更新频率是可配置的,但可以快至100 MHz或更高.这种快速更新或多或少地排除了基于睡眠的方法,因为阻塞睡眠太慢而不能在10纳秒(100MHz)的时间段内睡眠/唤醒.
所以我想要一个忙碌的等待.但是,忙等待应该对主线程尽可能友好:使用尽可能少的执行资源,因此尽可能少地增加主线程的开销.
我想这个想法将是一个长延迟指令,它不会使用很多资源,比如暂停,也有一个固定且已知的延迟.这将让我们校准“睡眠”时段,因此甚至不需要时钟读取(如果想要更新周期P,我们只需要为校准的忙碌睡眠发出这些指令的P / L.好暂停不符合后一个标准,因为它的延迟变化很大1.
第二种选择是使用长延迟指令,即使延迟未知,并且在每条指令执行rdtsc或其他时钟读取方法(clock_gettime等)之后,看看我们实际睡了多长时间.看起来它可能会减慢主线程的速度.
有更好的选择吗?
1暂停还有一些特定的语义来防止推测性内存访问,这可能会或可能不会对这个兄弟线程场景有益,因为我真的不在旋转等待循环中.
最佳答案 一些关于这个主题的随机思考.
因此,您希望在100 MHz样本上设置时间戳,这意味着在4GHz cpu上,每次呼叫之间有40个周期.
定时器线程忙于读取实时时钟(RTDSC ???)但不能使用带有cpuid的save方法,因为这需要100个周期.旧的实时时钟具有大约25的延迟(并且吞吐量为1/25),可能稍微更新,稍微更准确,具有稍微更多的延迟计时器(32个周期).
start:
read time (25 cycles)
tmp = time - last (1 cycle)
if tmp < sample length goto start
last += cycles between samples
sample = time
goto start
在一个完美的世界中,分支预测器每次都会猜测正确,实际上由于读取时间周期的变化,它将错误预测随机地向循环26个循环添加5-14个循环.
当写入样本时,另一个线程将从该高速缓存行的第一个推测性加载中取消其指令(记住对齐样本位置的64个字节,因此不会影响其他数据).并且在延迟~5-14个周期之后,样本时间戳的负载开始,具体取决于指令来自何处,循环缓冲区,微操作高速缓存或I高速缓存.
因此,除了另一个线程使用的cpu的一半之外,将丢失最多5个> 14个周期/ 40个周期的性能.
另一方面,在主线程中读取实时时钟会花费成本……
~1 / 4周期,延迟很可能被其他指令覆盖.但是你不能改变频率.除非在其之前有一些其他长延迟指令,否则25个周期的长延迟可能是个问题.
使用CAS指令(lock exch ???)可能会部分地解决问题,因为负载不应该导致重新发出指令,而是导致所有后续读写的延迟.