揭秘 Golang 通道、Goroutine 和最佳并发的测试源码


本github源码试图追问Golang 中的并发何时有意义,什么时候收益递减?

作者背景
当我深入研究 Golang 时,我想回答这个问题:Golang 中的并发何时有意义,什么时候收益递减?

因此,我创建了一个快速基准测试工具,使用Dispatcher -> Worker -> Job模式来描述一些简单但不同的作业类型 。这是一个基本的(有些过于简化的)框架,但它帮助我理解了 goroutines 和通道,以及如何以及何时使用它们。

对于 goroutines 和通道,我使用Dispatcher -> Worker -> Job模式创建了一个应用程序来对不同场景中的结果进行基准测试和比较。这些比较将使我能够了解相同的模式在不同类型的负载下如何运行,并有望揭示出某些情况下这是可遵循的最佳模式。相反,对于正确的场景数组,了解此模式何时无法提供我想要实现的好处也同样重要。

以下示例中使用的代码可在此处获得。

一个理想化的场景
第一次测试证实了我的期望:

  • 用 1 个Worker 运行 64 个一秒睡眠Job大约需要 64 秒。
  • 在 8 核机器上运行 64 个 1 秒睡眠Job和 8 个 worker 应该花费大约 1/8 的时间,即 8 秒。

从上面可以看出,这几乎精确到秒。同样,这是一个过于理想化的场景,但从简单开始并验证期望总是好的。随着Worker 的增加,所有Job工作的总处理时间都会变短,

这是有道理的。由于一项time.Sleep工作的开销很小,因此平均工作时间保持一致也是有道理的。

同样重要的是要注意,随着我们添加更多,每组worker 的平均内存使用量会增加:8 个worker 使用的内存量是 1 个worker 使用的内存量的 2 倍。
虽然现在内存是一种廉价资源,但如果应用程序开始逐渐接近系统上总可用内存的限制,请注意这种增加很重要。如果发生这种情况,我们可能会看到由于系统需要使用和管理交换空间而导致的开销大幅增加。

展望未来,如果此应用程序在 Docker 容器内运行,则该应用程序访问的资源可能远远少于系统资源总量。最佳 Docker 实践规定对容器资源设置限制,因为 Docker 环境可能会处理许多容器,所有容器都争相共享系统资源。

一个(更多)真实场景
虽然在理想化场景中验证我的期望是伟大的第一步,但对休眠应用程序进行基准测试远非真实世界的测试。我的下一个假设是,如果应用程序作业比低消耗的睡眠作业占用更多 CPU 和内存,那么上述结果可能会有所不同。

我决定创建一个工作job,将PI计算到n位。对于我的特定系统,我用来计算PI的函数花了大约1秒钟来计算10,000位。

与第一次测试中的理想场景相比,8 个worker 运行场景的速度仅为 3 倍,而不是 8.02 倍。

同样,这是有道理的,因为worker 正在执行的作业job需要更多的 CPU 和内存。虽然节省的时间远没有理想化场景那么多,但3 倍的收益仍然是一个相当大的优化!

此应用程序主要是 CPU 密集型作业。如果工作Job是处理大块数据的读写,Go 还必须管理更多的内存和 I/O 操作。

在PiJob示例中,1 名worker(平均工作时间:0.96 秒)的运行速度Pi Job 比 8 名worker(平均工作时间:2.53 秒)快 2.6 倍。
这种差异是为管理上下文切换和 goroutine 协调而产生的开销。
对于 8 个 worker,这个开销仍然比添加额外 worker 获得的性能要小得多。

收益递减点
在某些时候,管理 goroutine 所需的开销超过了更多并发 worker 的好处。如果我们启动的worker 数量多于runtime.NumCPU()要求的数量怎么办?这一次,我们将它推到 32 个worker ,而不是最多 8 个worker 。

随着添加的worker 线程数多于 CPU 内核数,收益递减。当然,超过 8 个worker 会有一点收益,但远不及之前看到的 3 倍飞跃。
事实上,32 worker 的平均工作时间比最佳 8 worker (2.51 秒)增加了 3.3 倍(8.37 秒),而总处理时间仅提高了 1.1 倍(3.1 秒)。

 I/O 密集型作业
在这种情况下,I/O 绑定任务并没有从并发中获益。虽然速度增加了大约1.2 倍,但由于并发开销,平均作业处理时间增加了 6.5 倍
再加上每个worker 都 I/O 绑定到同一个磁盘。

像这样的 I/O 密集型作业的收益递减点是立竿见影的。


结论
这种模式中的并发可以大大提高应用程序的吞吐量。

事实上,并发带来的性能提升可能取决于您的作业实际执行的任务。

虽然没有适用于所有情况的灵丹妙药,但像这样运行测试和基准测试可以帮助您找到最佳平衡点。如果您找不到合适的工具来发现您正在寻找的东西,请自己编写!