我的Go程序如何让所有CPU内核保持忙碌状态?

时间:2013-07-25 20:45:36

标签: multithreading go

Goroutines是轻量级进程,由Go运行时自动切片到一个或多个操作系统线程上。 (这是Go的非常酷的功能!)

假设我有一个像Web服务器一样的并发应用程序。在我的假设程序中同时发生了大量的事情,没有太多的非并发(Amdahl定律)比率。

目前使用的默认操作系统线程数似乎是1.这是否意味着只使用了一个CPU内核?

如果我用

开始我的程序
runtime.GOMAXPROCS(runtime.NumCPU())

是否可以合理有效地使用我的电脑上的所有核心?

使用甚至更多的 OS线程是否有任何“平行松弛”的好处,例如通过一些启发式

runtime.GOMAXPROCS(runtime.NumCPU() * 2)

3 个答案:

答案 0 :(得分:49)

来自Go FAQ:

  

为什么我的多goroutine程序不使用多个CPU?

     

您必须设置GOMAXPROCS shell环境变量或使用运行时包的类似命名的函数,以允许运行时支持使用多个OS线程。

     

执行并行计算的程序应该受益于GOMAXPROCS的增加。但是,请注意并发性不是并行性。

(2015年8月28日更新:Go 1.5设置为使GOMAXPROCS的默认值与您机器上的CPU数量相同,因此这不应该成为问题)

  

为什么使用GOMAXPROCS> 1有时会让我的程序变慢?

     

这取决于您的计划的性质。通过添加更多goroutine无法加速本质上顺序的问题。当问题本质上是并行时,并发只会变成并行性。

     

实际上,在使用多个OS线程时,花费更多时间在通道上进行通信而不是进行计算的程序将会出现性能下降。这是因为在线程之间发送数据涉及切换上下文,这具有显着的成本。例如,Go规范中的主筛网示例没有显着的并行性,尽管它启动了许多goroutines;增加GOMAXPROCS更有可能减慢速度,而不是加快速度。

     

Go的goroutine调度程序并不像它需要的那样好。将来,它应该识别这种情况并优化其对OS线程的使用。目前,应根据每个应用程序设置GOMAXPROCS。

简而言之:让Go使用“高效使用所有核心”非常困难。简单地产生十亿个goroutines并增加GOMAXPROCS就像降低你的性能一样,因为它会一直在切换线程上下文。如果你有一个可并行化的大型程序,那么将GOMAXPROCS增加到并行组件的数量就可以了。如果你在一个很大程度上非并行的程序中嵌入了并行问题,它可能会加速,或者你可能必须创造性地使用诸如runtime.LockOSThread()之类的函数来确保运行时正确地分配所有内容(一般来说,只是愚蠢地传播目前非阻塞的Goroutines在所有活动线程中随意均匀地发布。)

另外,GOMAXPROCS是要使用的CPU核心数,如果它大于NumCPU,我很确定它只是钳制到NumCPU。 GOMAXPROCS并不严格等于线程数。我并不完全确定运行时决定何时生成新线程,但是一个实例是当使用runtime.LockOSThread()的阻塞goroutines的数量大于或等于GOMAXPROC时 - 它将产生比核心更多的线程所以它可以保持程序的其余部分运行良好。

基本上,增加GOMAXPROCS并使用使用所有CPU核心非常简单。在Go的开发过程中,实际上将其用于智能且高效地使用 CPU的所有核心,这需要大量的程序设计和完成才能实现,这是另一回事。

答案 1 :(得分:4)

这个问题无法回答,问题太广泛了。

了解您的问题,算法和工作量,并衡量最适合此组合的内容。

没有人可以回答这样的问题:“有没有任何启发式方法,在我的午餐中添加两倍的盐会让它味道更好?”因为这取决于午餐(西红柿比草莓更有益于盐)你的口味和已经有多少盐。试试吧。

更多:runtime.GOMAXPROCS(runtime.NumCPU())已达到崇拜状态,但通过从外部设置GOMAXPROCS环境变量来控制线程数可能是更好的选择。

答案 2 :(得分:0)

runtime.GOMAXPROCS()设置程序可以同时使用的(虚拟)CPU核心数。允许Go使用比你实际拥有的更多CPU核心将无济于事,因为你的系统只有这么多CPU内核。

为了在多个线程中运行,您的程序必须有几个goroutine,通常是go someFunc()的函数调用。如果你的程序没有启动任何额外的goroutine,它将自然只在一个线程中运行,无论你允许它使用多少CPU /核心。

查看this以及有关如何创建goroutines的以下练习。