首页 > 解决方案 > 具有工作池的基准功能,没有工作人员但速度更快,用于测试结果

问题描述

我正在尝试使用工作池和不使用工作池来创建功能,之后我创建了基准测试以比较哪个想要更快,但我得到的结果是使用工作池的功能比没有工作需要更长的时间。

这是结果

goos: linux
goarch: amd64
BenchmarkWithoutWorker-4        4561        228291 ns/op       13953 B/op       1744 allocs/op
BenchmarkWithWorker-4           1561        651845 ns/op       54429 B/op       2746 allocs/op

工作池看起来很简单,我正在遵循这个 stackoverflow 问题中的示例, 这里是我的工作池的场景,没有

 var wg sync.WaitGroup
 
// i will get data from the DB, let say the data lenght about 1000
 const dataFromDB int = 1000 

// numOfProduce in benchmarking value is dataFromDB i defined
func WithoutWorker(numOfProduce int) {
     for i := 0; i < numOfProduce; i++ {
         if doSomething(fmt.Sprintf("data %d", i)) != nil {
             fmt.Println("error")
         }
     }
 }

 func WithWorker(numWorker int) {
     jobs := make(chan *Job, dataFromDB)
     result := make(chan *Result, 10)
     for i := 0; i < numWorker; i++ {
         wg.Add(1)
         go consume(i, jobs, result)
     }

     go produce(jobs)
     wg.Wait()
    
     // i might analyze the result channel 
     // here later to return any error to client if any error i got
 }

 func doSomething(str string) error {
     if str == "" {
         return errors.New("empty")
     }

     return nil
 }

 func consume(workerID int, jobs <-chan *Job, result chan<- *Result) {
     defer wg.Done()
     for job := range jobs {
         //log.Printf("worker %d", workerID)
         //log.Printf("job %v", job.ValueJob)
         err := doSomething(job.ValueJob)
         if err != nil {
             result <- &Result{Err: err}
         }
     }
 }

 func produce(jobs chan<- *Job) {
     for i := 1; i < dataFromDB; i++ {
         jobs <- &Job{
             Id:       i,
             ValueJob: fmt.Sprintf("data %d", i),
         }
     }
     close(jobs)
 }

我的工作人员池中是否缺少某些内容?

对于基准测试代码,它看起来像教程中的代码:) 只是调用函数的简单代码,我也添加b.ReportAllocs()

标签: goconcurrencybenchmarkingchannelgoroutine

解决方案


如果您在多个 goroutines/workers 上拆分的工作少于将作业发送到 goroutine 并接收结果的通信开销,那么在单台机器上完成工作会更快。

在您的示例中,您(几乎)没有做任何工作:

func doSomething(str string) error {
     if str == "" {
         return errors.New("empty")
     }

     return nil
}

将其拆分为多个 goroutine 会减慢速度。


举例说明:

如果你的工作需要 5ns(纳秒)并且你做了 1000 次你有

单核0.005ms

如果你将它分布在 10 个核心上,它将增加每个作业的通信开销。假设通信开销为 1 微秒 (1000ns)。现在你有 1000 个工作 * (5ns + 1000ns) / 10 个内核 =

10 核 0.1005ms

这只是一个包含一些虚构数字的示例,数学并不准确,但它应该说明这一点:只有当它(显着)小于工作本身的成本时,才值得引入沟通成本.


推荐阅读