10000次API调用耗时太长

问题描述 投票:0回答:1

我正在学习并尝试 Golang 中的

WaitGroup
功能。这是我的代码:

package main

import (
    "atomic"
    "http"
    "log"
    "sync"
    "time"
)

func makeRequest(n int) {
    wg := sync.WaitGroup{}
    count := atomic.Int32{}
    wg.Add(n)
    s := time.Now()
    for j := 0; j < n; j++ {
        go myGoRoutine(&wg, &count)
    }
    wg.Wait()
    log.Printf("Time Elapsed: %v for %d iterations", time.Since(s), n)
}

func myGoRoutine(wg *sync.WaitGroup, count *atomic.Int32) {
    defer wg.Done()
    _, err := http.Get("https://jsonplaceholder.typicode.com/todos/1")
    if err != nil {
        log.Printf("Error: %v", err.Error())
    }
    count.Add(1)
}
func main() {
    cases := []int{1, 10, 100, 1000, 10000}
    for _, c := range cases {
        makeRequest(c)
    }

}

这是输出:

2023/12/28 07:31:34 Time Elapsed: 936.169917ms for 1 iterations
2023/12/28 07:31:34 Time Elapsed: 16.452041ms for 10 iterations
2023/12/28 07:31:34 Time Elapsed: 37.336667ms for 100 iterations
2023/12/28 07:31:36 Time Elapsed: 2.037294792s for 1000 iterations
2023/12/28 07:32:23 Time Elapsed: 47.1717935s for 10000 iterations

由于所有 API 调用都是同时发生的,因此我预计所有 5 种情况所花费的时间即使不相同,也会相似。前 3 种情况确实如此,但 1000 次 API 调用需要 2 秒,10000 次 API 调用需要超过 45 秒。

我对 Golang 中的

WaitGroup
的理解才刚刚几天。很可能我在代码中犯了错误或者我的基本理解不正确。不管怎样,我将不胜感激任何帮助。

go concurrency goroutine waitgroup
1个回答
0
投票

总结

  • 如果需要,我们需要使用Benchmark测试来检查Goroutine的开销。
  • Goroutine 本身在并行执行多个高负载任务时是高效的。但如果负载太小,那么最好串行进行。
  • 你的代码不适合测试 Goroutine 性能,因为多次调用 API 会导致在后端服务器排队

详细

进行低负载任务的例行测试

  • 您可以看到 Go 例程在低负载任务下的性能比仅调用函数更差
func BenchmarkTest_makeRequest(b *testing.B) {
    tests := []struct {
        makeRequest func(n int)
    }{
        {
            makeRequest: makeRequestWithGoRoutine,
        },

        {
            makeRequest: makeRequestWithoutGoRoutine,
        },

        {
            makeRequest: makeRequestWithoutFunctionCall,
        },
    }
    for _, tt := range tests {
        b.Run("test", func(b *testing.B) {
            tt.makeRequest(b.N)
        })
    }
}

BenchmarkTest_makeRequest/test-12                5522967               206.9 ns/op
BenchmarkTest_makeRequest/test#01-12            79312916                15.42 ns/op
BenchmarkTest_makeRequest/test#02-12            163376266                6.478 ns/op

进行高负荷任务的例行测试

  • 您可以看到 Go 例程在处理高负载任务时比仅调用函数具有更高的性能
  • 为了模拟高负载,我刚刚添加了
    time.Sleep(time.Millisecond * 10)
    。参考号如何人为提高CPU使用率
func BenchmarkTest_makeRequestWithHighLoad(b *testing.B) {
    tests := []struct {
        makeRequest func(n int)
    }{
        {
            makeRequest: makeRequestWithGoRoutine,
        },

        {
            makeRequest: makeRequestWithoutGoRoutine,
        },
    }
    for _, tt := range tests {
        b.Run("test", func(b *testing.B) {
            tt.makeRequest(b.N)
        })
    }
}
BenchmarkTest_makeRequestWithHighLoad/test-12            2819386               409.7 ns/op
BenchmarkTest_makeRequestWithHighLoad/test#01-12             100          10787388 ns/op

Goroutine 中有哪些开销以及我们如何处理它?

其余代码

func myGoRoutine(wg *sync.WaitGroup, count *int32) {
    defer wg.Done()
    atomic.AddInt32(count, 1)
}
func myGoRoutineWithHighLoad(wg *sync.WaitGroup, count *int32) {
    defer wg.Done()
    time.Sleep(time.Millisecond * 10)
    atomic.AddInt32(count, 1)
}

func makeRequestWithGoRoutine(n int) {
    wg := sync.WaitGroup{}
    var count int32
    wg.Add(n)
    for j := 0; j < n; j++ {
//      go myGoRoutine(&wg, &count)
        go myGoRoutineWithHighLoad(&wg, &count)
    }
    wg.Wait()
}

func makeRequestWithoutGoRoutine(n int) {
    wg := sync.WaitGroup{}
    var count int32
    wg.Add(n)
    for j := 0; j < n; j++ {
//      myGoRoutine(&wg, &count)
        myGoRoutineWithHighLoad(&wg, &count)
    }
    wg.Wait()
}

func makeRequestWithoutFunctionCall(n int) {
    wg := sync.WaitGroup{}
    var count int32
    wg.Add(n)
    for j := 0; j < n; j++ {
        count++
        wg.Done()
    }
    wg.Wait()
}
© www.soinside.com 2019 - 2024. All rights reserved.