工作池的最佳大小
Optimal size of worker pool
我正在构建一个使用 goroutine 的“工作池”的 Go 应用程序,最初我启动池创建一些工作人员。我想知道多核处理器中的最佳工作人员数量是多少,例如在具有 4 个内核的 CPU 中?我目前正在使用以下方法:
// init pool
numCPUs := runtime.NumCPU()
runtime.GOMAXPROCS(numCPUs + 1) // numCPUs hot threads + one for async tasks.
maxWorkers := numCPUs * 4
jobQueue := make(chan job.Job)
module := Module{
Dispatcher: job.NewWorkerPool(maxWorkers),
JobQueue: jobQueue,
Router: router,
}
// A buffered channel that we can send work requests on.
module.Dispatcher.Run(jobQueue)
完整的实现在
job.NewWorkerPool(maxWorkers)
and
module.Dispatcher.Run(jobQueue)
我使用工作池的用例:我有一个服务接受请求并调用多个外部 APIs 并将它们的结果聚合到一个响应中。每个调用都可以独立于其他调用完成,因为结果的顺序无关紧要。我将调用分派给工作池,其中每个调用都以异步方式在一个可用的 goroutine 中完成。我的“请求”线程在工作线程完成后立即继续监听 return 通道,同时获取和汇总结果。完成所有操作后,最终聚合结果将作为响应 returned。由于每个外部 API 调用都可能呈现不同的响应时间,因此某些调用可以比其他调用更早完成。根据我的理解,以并行方式执行它在性能方面会更好,就好像与以同步方式调用每个外部 API 一个接一个
相比
示例代码中的注释表明您可能将 GOMAXPROCS
和工作池这两个概念混为一谈。这两个概念在 Go 中是完全不同的。
GOMAXPROCS
设置 Go 运行时将使用的最大 CPU 线程数。这默认为系统上找到的 CPU 个核心数,几乎永远不应更改。我唯一能想到改变它的是,如果你出于某种原因想要明确限制 Go 程序使用少于可用的 CPUs,那么你可以将它设置为 1,例如,即使运行 四核 CPU。这应该只在极少数情况下才重要。
TL;DR; 切勿手动设置 runtime.GOMAXPROCS
。
Go 中的工作池是一组 goroutine,它们在作业到达时处理它们。在 Go 中有不同的处理工作池的方法。
您应该使用多少工人?没有 objective 答案。可能唯一知道的方法是对各种配置进行基准测试,直到找到满足您要求的配置。
作为一个简单的案例,假设您的工作池正在做一些非常 CPU 密集的事情。在这种情况下,您可能需要每个 CPU.
一名工人
不过,作为一个更有可能的例子,假设您的工作人员正在做更多 I/O 绑定的事情——例如读取 HTTP 请求或通过 SMTP 发送电子邮件。在这种情况下,您可以合理地处理每个 CPU.
数十个甚至数千个工人
然后还有一个问题,即您是否应该使用工作池。 Go 中的大多数问题根本不需要工作池。我参与过数十个生产 Go 程序,但从未在其中任何一个中使用过工作池。我也写了很多次一次性使用的 Go 工具,而且可能只用过一次工作池。
最后,GOMAXPROCS
和工作池的唯一关联方式与 goroutines 与 GOMAXPROCS
的关联方式相同。来自 the docs:
The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit.
从这个简单的描述中,很容易看出 goroutine 的数量可能比 GOMAXPROCS
多得多(可能有数十万...或更多)--GOMAXPROCS
仅限制了 "operating system threads that can execute user-level Go code simultaneously"--此时不执行用户级 Go 代码的 goroutines 不算。在 I/O 中,绑定的 goroutines(例如等待网络响应的 goroutines)不执行代码。所以理论上 goroutine 的最大数量仅受系统可用内存的限制。
我正在构建一个使用 goroutine 的“工作池”的 Go 应用程序,最初我启动池创建一些工作人员。我想知道多核处理器中的最佳工作人员数量是多少,例如在具有 4 个内核的 CPU 中?我目前正在使用以下方法:
// init pool
numCPUs := runtime.NumCPU()
runtime.GOMAXPROCS(numCPUs + 1) // numCPUs hot threads + one for async tasks.
maxWorkers := numCPUs * 4
jobQueue := make(chan job.Job)
module := Module{
Dispatcher: job.NewWorkerPool(maxWorkers),
JobQueue: jobQueue,
Router: router,
}
// A buffered channel that we can send work requests on.
module.Dispatcher.Run(jobQueue)
完整的实现在
job.NewWorkerPool(maxWorkers) and module.Dispatcher.Run(jobQueue)
我使用工作池的用例:我有一个服务接受请求并调用多个外部 APIs 并将它们的结果聚合到一个响应中。每个调用都可以独立于其他调用完成,因为结果的顺序无关紧要。我将调用分派给工作池,其中每个调用都以异步方式在一个可用的 goroutine 中完成。我的“请求”线程在工作线程完成后立即继续监听 return 通道,同时获取和汇总结果。完成所有操作后,最终聚合结果将作为响应 returned。由于每个外部 API 调用都可能呈现不同的响应时间,因此某些调用可以比其他调用更早完成。根据我的理解,以并行方式执行它在性能方面会更好,就好像与以同步方式调用每个外部 API 一个接一个
相比示例代码中的注释表明您可能将 GOMAXPROCS
和工作池这两个概念混为一谈。这两个概念在 Go 中是完全不同的。
GOMAXPROCS
设置 Go 运行时将使用的最大 CPU 线程数。这默认为系统上找到的 CPU 个核心数,几乎永远不应更改。我唯一能想到改变它的是,如果你出于某种原因想要明确限制 Go 程序使用少于可用的 CPUs,那么你可以将它设置为 1,例如,即使运行 四核 CPU。这应该只在极少数情况下才重要。TL;DR; 切勿手动设置
runtime.GOMAXPROCS
。Go 中的工作池是一组 goroutine,它们在作业到达时处理它们。在 Go 中有不同的处理工作池的方法。
您应该使用多少工人?没有 objective 答案。可能唯一知道的方法是对各种配置进行基准测试,直到找到满足您要求的配置。
作为一个简单的案例,假设您的工作池正在做一些非常 CPU 密集的事情。在这种情况下,您可能需要每个 CPU.
一名工人不过,作为一个更有可能的例子,假设您的工作人员正在做更多 I/O 绑定的事情——例如读取 HTTP 请求或通过 SMTP 发送电子邮件。在这种情况下,您可以合理地处理每个 CPU.
数十个甚至数千个工人然后还有一个问题,即您是否应该使用工作池。 Go 中的大多数问题根本不需要工作池。我参与过数十个生产 Go 程序,但从未在其中任何一个中使用过工作池。我也写了很多次一次性使用的 Go 工具,而且可能只用过一次工作池。
最后,GOMAXPROCS
和工作池的唯一关联方式与 goroutines 与 GOMAXPROCS
的关联方式相同。来自 the docs:
The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit.
从这个简单的描述中,很容易看出 goroutine 的数量可能比 GOMAXPROCS
多得多(可能有数十万...或更多)--GOMAXPROCS
仅限制了 "operating system threads that can execute user-level Go code simultaneously"--此时不执行用户级 Go 代码的 goroutines 不算。在 I/O 中,绑定的 goroutines(例如等待网络响应的 goroutines)不执行代码。所以理论上 goroutine 的最大数量仅受系统可用内存的限制。