multithreading - 为什么 goroutine 分配在多核上会变慢?

标签 multithreading go

我在用 Go 做一些实验,我发现了一些非常奇怪的东西。当我在我的计算机上运行以下代码时,它会在大约 0.5 秒内执行。

package main

import (
  "fmt"
  "runtime"
  "time"
)
func waitAround(die chan bool) {
  <- die
}
func main() {
  var startMemory runtime.MemStats
  runtime.ReadMemStats(&startMemory)

  start := time.Now()
  cpus := runtime.NumCPU()
  runtime.GOMAXPROCS(cpus)
  die := make(chan bool)
  count := 100000
  for i := 0; i < count; i++ {
    go waitAround(die)
  }
  elapsed := time.Since(start)

  var endMemory runtime.MemStats
  runtime.ReadMemStats(&endMemory)

  fmt.Printf("Started %d goroutines\n%d CPUs\n%f seconds\n",
    count, cpus, elapsed.Seconds())
  fmt.Printf("Memory before %d\nmemory after %d\n", startMemory.Alloc,
    endMemory.Alloc)
  fmt.Printf("%d goroutines running\n", runtime.NumGoroutine())
  fmt.Printf("%d bytes per goroutine\n", (endMemory.Alloc - startMemory.Alloc)/uint64(runtime.NumGoroutine()))

  close(die)
}

但是,当我使用 runtime.GOMAXPROCS(1) 执行它时,它执行得更快(~0.15 秒)。任何人都可以向我解释为什么使用更多内核运行许多 goroutine 会更慢吗?将 goroutine 多路复用到多个内核上是否有任何重大开销?我意识到 goroutines 没有做任何事情,如果我必须等待例程实际做某事,情况可能会有所不同。

最佳答案

在单核上运行时,goroutine的分配和切换只是内部记账的事情。 Goroutines 永远不会被抢占,所以切换逻辑非常简单而且非常快。更重要的是,在这种情况下,您的主例程根本不会产生,因此 goroutines 在终止之前甚至都不会开始执行。您分配结构然后将其删除,仅此而已。 (编辑对于较新版本的 go 来说可能不是这样,但只有 1 个进程肯定更有序)

但是当您将例程映射到多个线程时,您会突然涉及到操作系统级别的上下文切换,这会慢几个数量级并且更复杂。即使你在多核上,也有很多工作要做。另外,现在您的 gouroutines 可能实际上在程序终止之前正在运行。

在这两种情况下尝试strace程序,看看它的行为有何不同。

关于multithreading - 为什么 goroutine 分配在多核上会变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16021165/

相关文章:

java - 一次提交多个相关任务的线程池的推荐大小

.net - .net多线程Winforms

http - 仅下载文件的一部分

go - 如何从 gorilla 商店中删除用户 session ?

json - 动态创建JSON对象

wpf - 在 WPF 应用程序中使用每个 session WCF 服务

c - C 中 pthread 不退出

java - 原子能保证可见性吗? java

json - 如何让golang打印出JSON中的所有字段?

go - Hugo 如何维护站点范围的数据,例如 .Site.AllPages?