1

一些上下文:

我正在尝试调试 GoGRPC 服务器,特定的 API 调用似乎需要很多时间。这个调用对 Kafka 进行了多次读取(比如说 10-20 次),所以我预计它需要一些时间,只是没有那么多。

1 个 API 调用大约需要 1-3 秒才能完成,但如果我在脚本中进行 40 个 api 调用,则完成所有这些调用几乎需要 30 秒。但它并没有像我预期的那样“同时”完成它们,第一个需要 5 秒,而后面的则每秒吐出 1 个左右。

它需要 29 秒并一次响应所有 40 个请求。当请求时间过长时,这会导致 API 调用者超时。

我正在尝试分析 CPU 以查看我在哪里花费时间。但我对此并不陌生,go profiler 的输出并没有多大意义。

我用 生成了图表go tool pprof,但在解释输出时遇到了一些问题。

CPU 调用图

  1. 有一个框描述时间、类型、buildID 等。Duration在这个框中,是描述 CPU 运行的总时间吗?不包括等待时间
  2. 有两种类型的边缘,实线和虚线。有什么区别?边缘标记的时间是什么意思?
  3. 箭头的方向是否意味着呼叫方向?例如,函数 A 调用 B,在图上它将是 A -> B?
  4. 每个顶点在底部都有一个时间,例如 0.49s(45.79%) 中的 0.01s(0.93%),这个时间是什么意思?

块配置文件

  1. 与上面的#4 相同,边和顶点都有时间。这是什么意思?

编辑:

我的服务器所做的是从 kafka 流中检索一些数据。我已经确定了慢的原因,并且我编写了一个只有 kafka 调用函数的脚本。这是脚本和块分析图。

每次消费到 kafka 大约需要 50-100 毫秒,但由于大部分时间都花在做 IO 上,我预计 API 的吞吐量实际上会很高,事实并非如此。如果我打 100 个电话,大约需要 3 秒,如果我打 400 个电话,大约需要 10 秒。试图了解如何加快 API 的吞吐量

func main() {

    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()

    block, _ := os.Create("block.prof")
    runtime.SetBlockProfileRate(1)
    p := pprof.Lookup("block")
    defer p.WriteTo(block, 0)

    var wg sync.WaitGroup
    wg.Add(messageCount)
    for i := 0; i < messageCount; i++ {
        go func() {
            // consume()
            withConsumer()
            wg.Done()
        }()

    }

    wg.Wait()
}

var servers = []string{"kafka-1", "kafka-2", "kafka-3"}

var count = 0
var partition = int32(0)

func consume() {

    index := count
    t := time.Now()
    count++
    fmt.Println("starting consume", index)
    defer func() {
        fmt.Println("consume ", index, "took", time.Since(t).String())
    }()

    consumer, err := sarama.NewConsumer(servers, nil)
    if err != nil {
        panic(err)
    }
    var max, min int64
    max = 1431401
    min = 2

    defer func() {
        consumer.Close()
    }()

    pc, err := consumer.ConsumePartition("source-topic", partition, rand.Int63n(max-min)+min)
    if err != nil {
        panic(err)
    }

    defer func() {
        pc.Close()
    }()

    signals := make(chan os.Signal, 1)
    signal.Notify(signals, os.Interrupt)

    select {
    case msg := <-pc.Messages():
        fmt.Println("msg: ", len(msg.Value))
    case <-signals:
        return
    }
}

在此处输入图像描述 谢谢,

4

1 回答 1

0
  1. 有一个描述时间、类型、buildID 等的框。这个框中的 Duration 是描述 CPU 运行的总时间吗?不包括等待时间

正确的。 https://golang.org/doc/diagnostics#profiling说:

cpu:CPU 配置文件确定程序在主动消耗 CPU 周期(而不是在睡眠或等待 I/O 时)花费时间的位置。

  1. 有两种类型的边缘,实线和虚线。有什么区别?边缘标记的时间是什么意思?

https://github.com/google/pprof/issues/493说“虚线/虚线表示中间节点已被删除。节点被删除以保持图形足够小以进行可视化。”

https://gperftools.github.io/gperftools/cpuprofile.html说“每个边缘都标有被调用者代表调用者花费的时间。”

  1. 箭头的方向是否意味着呼叫方向?例如,函数 A 调用 B,在图上它将是 A -> B?

是的。

  1. 每个顶点在底部都有一个时间,例如 0.49s(45.79%) 中的 0.01s(0.93%),这个时间是什么意思?

第一次是当地时间。第二次是累计时间。 https://gperftools.github.io/gperftools/cpuprofile.html详细阐述:

“本地”时间是执行直接包含在过程中的指令(以及内联到过程中的任何其他过程)所花费的时间。“累积”时间是“本地”时间和在任何被调用者中花费的时间的总和。如果累计时间与当地时间相同,则不打印。

于 2021-06-09T22:34:19.517 回答