go - Go 中令人困惑的并发和性能问题

标签 go concurrency parallel-processing factorial goroutine

现在我通过观看开始学习 Go 语言 this great course .需要明确的是,多年来我只写 PHP,并发/并行对我来说是新的,所以我对此有点困惑。

在本类(class)中,有一个任务是创建一个程序来计算 100 次阶乘。我更进一步,为了比较性能,我将其更改为 10000,出于某种原因,顺序程序的运行速度与并发相同甚至更快。

在这里我将提供 3 种解决方案:我的、教师的和顺序的

我的解决方案:

package main

import (
 "fmt"
)

func gen(steps int) <-chan int{
     out := make(chan int)
     go func() {
         for j:= 0; j <steps; j++ {
             out <- j
         }
         close(out)
      }()
      return out
}

func factorial(in <-chan int) <-chan int {
    out := make(chan int) 
    go func() {
        for n := range in {
            out <-  fact(n)
        }
        close(out)
    }()
    return out
}

func fact(n int) int {
    total := 1
    for i := n;i>0;i-- {
        total *=i
    }
    return total
}

func main() {
    steps := 10000
    for i := 0; i < steps; i++ {
        for n:= range factorial(gen(10)) {
            fmt.Println(n)
        }
     }
}

执行时间:

  • 真正的 0m6,356s
  • 用户 0m3,885s
  • 系统 0m0,870s

教师解答: 包主

import (
 "fmt"
)

func gen(steps int) <-chan int{
    out := make(chan int)
    go func() {
        for i := 0; i < steps; i++ {
            for j:= 0; j <10; j++ {
                out <- j
            }
        }
        close(out)
    }()
    return out
}

func factorial(in <-chan int) <-chan int {
    out := make(chan int) 
    go func() {
        for n := range in {
            out <-  fact(n)
        }
        close(out)
    }()
    return out
}

func fact(n int) int {
    total := 1
    for i := n;i>0;i-- {
        total *=i
    }
    return total
}

func main() {
    steps := 10000
    for n:= range factorial(gen(steps)) {
        fmt.Println(n)
    }
}

执行时间:

  • 实际 0m2,836s
  • 用户 0m1,388s
  • 系统 0m0,492s

顺序:

package main

import (
 "fmt"
)

func fact(n int) int {
    total := 1
    for i := n;i>0;i-- {
        total *=i
    }
    return total
}

func main() {
    steps := 10000
    for i := 0; i < steps; i++ {
        for j:= 0; j <10; j++ {
            fmt.Println(fact(j))
        }
    }
}

执行时间:

  • 实际 0m2,513s
  • 用户 0m1,113s
  • 系统 0m0,387s

因此,如您所见,顺序解决方案最快,老师的解决方案第二,我的解决方案第三。

第一个问题:为什么顺序解法最快? 第二,为什么我的解决方案这么慢?如果我在我的解决方案中理解正确,我将在 gen 中创建 10000 个 goroutines和 10000 里面 factorial在教师解决方案中,他只在 gen 中创建了 1 个 goroutine和 1 在 factorial .我这么慢是因为我创建了太多不需要的 goroutines?

最佳答案

这是并发性和并行性之间的区别 - 你的,你的老师和顺序在设计中逐渐减少并发,但它们的并行程度取决于 CPU 内核的数量,并且存在与并发相关的设置和通信成本。代码中没有异步调用,因此只有并行才能提高速度。

这值得一看:https://blog.golang.org/concurrency-is-not-parallelism

此外,即使使用并行内核,加速也将取决于工作负载的性质 - 谷歌阿姆达尔定律进行解释。

关于go - Go 中令人困惑的并发和性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49937803/

相关文章:

java - 有容量的延迟队列

java - 当我执行多个优于 Runtime.getRuntime().availableProcessors()) 的并行线程时,为什么我的 Java 程序运行得更快?

concurrency - 使用 Go 通过 channel 发送 channel

java - 限制 SQL Server 2005 中的 SELECT 语句

windows - 微软 Windows : is there a way to limit the number of commands running in parallel?

multithreading - 当线程只写入同一个缓存 block 时,是否也会发生错误共享?

go - 使用模块时在工作区中制作 VSCode 安装包

html - golang html 模板不显示任何内容

github - CircleCI & Golang - 无法导入 AWS SDK

go - 如何反序列化 Kubernetes YAML 文件