有工作池和没有工作池的基准函数,但没有工作池的测试结果更快
benchmark function with worker pool and without but faster without worker for test result
我正在尝试使用工作池和不使用工作池来实现功能,之后我创建了基准测试以比较需要更快的速度,但我得到的结果是使用工作池的功能比没有使用工作池的时间更长。
这是结果
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
工作池看起来很简单,我正在按照 中的示例进行操作
这是我的工作人员池的情况,没有
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()
如果您在多个 goroutines/worker 上拆分的工作少于将作业发送到 goroutine 并接收结果的通信开销,那么在一台机器上完成工作会更快。
在您的示例中,您(几乎)没有做任何工作:
func doSomething(str string) error {
if str == "" {
return errors.New("empty")
}
return nil
}
将其拆分到多个 goroutine 上会减慢速度。
举例说明:
如果您的工作需要 5ns(纳秒)并且您执行了 1000 次,那么您就拥有了
0.005ms on a single core
如果您将它分布在 10 个核心上,则会增加每个作业的通信开销。假设通信开销为 1 微秒 (1000ns)。现在你有 1000 个作业 * (5ns + 1000ns) / 10 个核心 =
0.1005ms on 10 cores
这只是一些虚构数字的例子,数学并不精确,但它应该说明了这一点:只有当它(显着)小于工作本身的成本。
我正在尝试使用工作池和不使用工作池来实现功能,之后我创建了基准测试以比较需要更快的速度,但我得到的结果是使用工作池的功能比没有使用工作池的时间更长。
这是结果
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
工作池看起来很简单,我正在按照
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()
如果您在多个 goroutines/worker 上拆分的工作少于将作业发送到 goroutine 并接收结果的通信开销,那么在一台机器上完成工作会更快。
在您的示例中,您(几乎)没有做任何工作:
func doSomething(str string) error {
if str == "" {
return errors.New("empty")
}
return nil
}
将其拆分到多个 goroutine 上会减慢速度。
举例说明:
如果您的工作需要 5ns(纳秒)并且您执行了 1000 次,那么您就拥有了
0.005ms on a single core
如果您将它分布在 10 个核心上,则会增加每个作业的通信开销。假设通信开销为 1 微秒 (1000ns)。现在你有 1000 个作业 * (5ns + 1000ns) / 10 个核心 =
0.1005ms on 10 cores
这只是一些虚构数字的例子,数学并不精确,但它应该说明了这一点:只有当它(显着)小于工作本身的成本。