我们可以,并且应该将 OS 线程固定到 CPU 内核吗?

Ici*_*leF 1 cpu multithreading go

我正在用 Go(和 C)编程。场景是:C 中有每个线程的事件通道。有时我从 Go 中调用 cgo,将请求发布到当前线程的通道。完成事件在几微秒后从同一通道返回,我调用 cgo 来轮询它们。

当然,当提交请求的 goroutine 轮询完成时,它可以在另一个线程中,但我们现在先忽略这个问题。我的问题是:在纯 C 中,我们可以调用pthread_setaffinity_np将轮询线程固定到 CPU 内核以减少延迟(假设 #threads <= #cores)。在 Go 中,我们应该在有 goroutines 的情况下这样做吗?

  • 如果是,我们该怎么做?调用runtime.LockOSThread()多个 goroutine 来获取足够的 OS 线程,并通过 cgo 调用将它们固定在不同的内核上?
  • 如果没有,为什么?

PS 我也对 Go 的调度程序(GMP 模型)有所了解。不过好像Ms并没有绑定CPU核,Ps和物理核没有关系。

bcm*_*lls 5

从 C 到 Go 的调用使用调用者的线程,因此如果 C 线程已经锁定到 CPU,那么当它进入 Go 运行时时,它将保持不变。(为了演示该属性,您可以在 Linux 本地运行https://play.golang.org/p/2C9nxyohA91上的程序;Go Playground 不支持 cgo。)

您可以使用 将 Goroutine 锁定到线程runtime.LockOSThread,然后您可以通过pthread_setaffinity_np使用 cgo调用来设置 CPU 关联性。

但我不认为这会在延迟方面提供太多改进(如果有的话):C 和 Go 之间的转换已经增加了相当多的延迟(由于 Go 运行时调度程序中的缓存争用更新元数据等因素)。如果您在足够短的时间间隔内轮询 CPU 关联性很重要,那么您可能会以任何一种方式搅动 CPU 缓存;如果您处于足够长的时间间隔内,那么 CPU 亲和性的影响将与轮询间隔的影响相形见绌。

通过从基于轮询的方法切换到基于推送的方法,您可能会获得更多改进:要么使用 API 块的 C 部分,直到请求完成(并通过返回 Go 表示完成),或者使用 C部分回调到 Go 中直接通知原始 goroutine,例如通过关闭通道。(在 Go 1.17 中,您将能够使用 acgo.Handle获取一个值,您可以传递给 C 以引用 Go 分配的值。)