multithreading - 为超线程创建友好的定时忙循环

标签 multithreading performance concurrency x86 hyperthreading

想象一下,我想让一个主线程和一个辅助线程在同一个物理内核上作为两个超线程运行(可能通过强制它们的亲和力来大致确保这一点)。

主线程将执行重要的高 IPC、CPU 密集型工作。辅助线程除了定期更新主线程将定期读取的共享时间戳值外,什么都不做。更新频率是可配置的,但可以快到 100 MHz 或更高。这种快速更新或多或少排除了基于 sleep 的方法,因为阻塞 sleep 太慢而无法在 10 纳秒 (100 MHz) 周期内 sleep /唤醒。

所以我想要一个忙碌的等待。但是,忙等待应该对主线程尽可能友好:尽可能少地使用执行资源,从而尽可能少地增加主线程的开销。

我想这个想法是一个不使用很多资源的长延迟指令,比如 pause这也有一个固定的和已知的延迟。这将使我们校准“ sleep ”周期,因此甚至不需要读取时钟(如果想要更新周期 P,我们只需发出这些指令的 P/L 来校准忙碌 sleep 。好吧 pause 不需要满足后一个标准,因为它的延迟变化很大1。

第二种选择是即使延迟未知也使用长延迟指令,并且在每条指令之后执行 rdtsc或其他一些时钟读取方法( clock_gettime 等)来查看我们实际睡了多长时间。似乎它可能会大大减慢主线程的速度。

还有更好的选择吗?

1 还有 pause有一些关于防止推测性内存访问的特定语义,这可能对这个兄弟线程场景有益,也可能没有,因为我真的不在自旋等待循环中。

最佳答案

关于这个主题的一些随机沉思。

因此,您希望在 100 MHz 样本上有一个时间戳,这意味着在 4GHz cpu 上,每次调用之间有 40 个周期。

定时器线程忙于读取实时时钟(RTDSC???),但不能使用带有 cpuid 的保存方法,因为这需要 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

在完美的世界中,分支预测器每次都会猜对,实际上,由于读取时间周期的差异,它会错误预测随机将 5-14 个周期添加到循环 26 个周期。

当样本被写入时,另一个线程将从这个缓存行的第一个推测加载中取消它的指令(记住将样本位置对齐到 64 字节,这样其他数据不会受到影响)。采样时间戳的加载在大约 5-14 个周期的延迟后重新开始,具体取决于指令来自何处、循环缓冲区、微操作缓存或 I 缓存。

因此,除了另一个线程使用一半的 cpu 之外,最小 5->14 个周期/40 个周期的性能将丢失。

另一方面,在主线程中读取实时时钟将花费......

~1/4 周期,延迟很可能会被其他指令覆盖。但是你不能改变频率。 25 个周期的长延迟可能是一个问题,除非在它之前有其他一些长延迟指令。

使用 CAS 指令(lock exch???)可能会部分解决问题,因为负载不应该导致指令重新发出,而是导致所有后续读取和写入的延迟。

关于multithreading - 为超线程创建友好的定时忙循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45556708/

相关文章:

multithreading - 在多线程环境中锁定什么和不锁定什么(信号量和共享内存)

c# - 将 ManualResetEvent 包装为等待任务

multithreading - 原子操作有多昂贵?

haskell - 什么时候可以保证 MVar 操作不间断?

java - 线程安全的多生产者问题

C# NLog 性能类变量

c - 为什么 gcc 数学库效率这么低?

Azure Web App 的性能测试

java - 新线程 JDialog 中的 JProgressBar

java - ExecutorService.invokeAll 和关闭