缓冲通道的理想大小和工作人员数量是多少?

What's the ideal size of buffered channel and number of workers?

我正在尝试构建一个异步编解码器。我已经实现了一个可以访问缓冲的作业通道的作业调度程序

var JobChannel chan Job = make(chan Job, 100000)

调度员将工人数量作为输入并向他们分配工作

func StartDispacher(numberOfWorkers int){
    // start workers
    wg := &sync.WaitGroup{}
    wg.Add(numberOfWorkers)
    for i := int(1); i <= numberOfWorkers; i++ {
        go func(i int) {
            defer wg.Done()
            for j := range JobChannel {
                doWork(i, j)
            }
        }(i)
    }
}

我的主要功能启动调度程序并不断给它工作(在本例中为 200000 个工作)

workDispatcher.StartDispacher(2*runtime.NumCPU())
for i := 0; i < 200000; i++ {
    j := workDispatcher.Job{
        BytePacket: d,
        JobType:    workDispatcher.DECODE_JOB,
    }
    workDispatcher.JobChannel <- j
}

经过试验:结果有 2 个因素影响此代码的性能

是否有一种标准方法可以找到这些参数的最佳值,是否可以使这些值独立于机器的物理设置运行代码?

答案是否定的。最佳设置不仅取决于您 运行 在 doWork 中使用的软件(CPU 强度和 IO 功能将取决于),还取决于您的硬件可以执行多少指令和您的系统可以处理多少 IO。

这意味着它可能取决于您的系统是否安装了 SSD,甚至取决于您的带宽(如果您的系统执行涉及互联网访问的活动),您的 CPU(s) 有多少物理核心,等等...

您始终需要进行测量以确定系统在负载下的性能。这里的好消息是你只有 2 个变量,它们大部分是独立的,所以推理起来相当容易。

工作人员的数量决定了您的并发性,因此对处理进行基准测试以查看最佳并发性是多少。通常有许多并发进程,超过这些进程 returns 会急剧下降。

通道的大小与系统中任何其他通道的大小一样 "buffer"。更大的缓冲区可以处理更大的输入峰值,但可能会导致更大的延迟和内存使用。

在实践中,我发现三个缓冲区大小很重要:0、1 和“发送总数的上限”。

0 给出同步行为。

1 提供异步行为:它在 select 语句和 default 情况下很有用。

发送总数的上限提供了有保证的非阻塞行为:您可以在没有 select 的情况下发送给它,而不会有 goroutine 泄漏的风险。

其他数字可能会提供略微更好的吞吐量,但在规模上它们仍然会在包含通道内部互斥锁的缓存行上竞争,并且它们更有可能掩盖潜在的死锁和 goroutine 泄漏。