go - goroutines中的请求时间

标签 go timing goroutine

我正在尝试计算发出并发请求所需的时间。我的计时结果比 ab2 慢了大约四倍报告。

我尝试过两种不同的方式来计时请求,这两种方式都会导致类似的结果(与 ab2 的结果相去甚远)。举个例子,ab2 将在本地服务器上报告最长请求持续 2 毫秒,而此代码将报告最多 4.5 毫秒。顺便说一句,整个代码库可用here .

如何在 go 中正确计时请求?

方法一:定时不仅仅包括请求

来自 this commit .

// Let's spawn all the requests, with their respective concurrency.
wg.Add(r.Repeat)
r.doneWg.Add(r.Repeat)
for rno := 1; rno <= r.Repeat; rno++ {
    go func(no int, greq goreq.Request) {
        r.ongoingReqs <- struct{}{} // Adding sentinel value to limit concurrency.
        startTime := time.Now()
        greq.Uri = r.URL.Generate()
        gresp, err := greq.Do()
        if err != nil {
            log.Critical("could not send request to #%d %s: %s", no, r.URL, err)
        } else if no < r.Repeat {
            // We're always using the last response for the next batch of requests.
            gresp.Body.Close()
        }
        <-r.ongoingReqs // We're done, let's make room for the next request.
        resp := Response{Response: gresp, duration: time.Now().Sub(startTime)}
        // Let's add that request to the list of completed requests.
        r.doneChan <- &resp
        runtime.Gosched()
    }(rno, greq)
}

方法二:使用带defer语句的内部函数

来自 this commit .

// Let's spawn all the requests, with their respective concurrency.
wg.Add(r.Repeat)
r.doneWg.Add(r.Repeat)
for rno := 1; rno <= r.Repeat; rno++ {
    go func(no int, greq goreq.Request) {
        r.ongoingReqs <- struct{}{} // Adding sentinel value to limit concurrency.
        greq.Uri = r.URL.Generate()
        var duration time.Duration
        gresp, err := func(dur *time.Duration) (gresp *goreq.Response, err error) {
            defer func(startTime time.Time) { *dur = time.Now().Sub(startTime) }(time.Now())
            return greq.Do()
        }(&duration)

        if err != nil {
            log.Critical("could not send request to #%d %s: %s", no, r.URL, err)
        } else if no < r.Repeat {
            // We're always using the last response for the next batch of requests.
            gresp.Body.Close()
        }
        <-r.ongoingReqs // We're done, let's make room for the next request.
        resp := Response{Response: gresp, duration: duration}
        // Let's add that request to the list of completed requests.
        r.doneChan <- &resp
        runtime.Gosched()
    }(rno, greq)
}

我看过this question , 这没有帮助。

最佳答案

当您的 goroutines 进入系统调用(写入套接字)时,它们会被抢占。这意味着它们被打断了,另一个 goroutine 将在它们的位置运行。最终,你被抢占的 goroutine 将被再次调度,并从中断的地方继续运行。不过,这不一定恰好在系统调用完成后发生。

在 goroutine 中计时是很困难的,因为即使你串行地做所有事情,Go 1.5 的垃圾收集器也会偶尔运行,打断你理论上的串行循环。

唯一真正的解决方案有点复杂:

  1. 使用RawSyscall直接。
  2. 使用 //go:nosplit 注释您的函数,以防止它被抢占。
  3. 禁用垃圾收集器。

即便如此,我也可能会忘记一些事情。

关于go - goroutines中的请求时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32780612/

相关文章:

parallel-processing - goroutine 是否适用于大型、并行、受计算限制的问题?

Goroutine实现疑惑

Go Mod Private Repo导入路径(找不到模块)

go - Go 编译错误 : cannot use new(SimpleChaincode)

go - 如何将地址转换为类型并在 GDB for Golang 中打印它

c# - 如何在 C# 中编写 Tcp/ip 网络并在 Golang 中编写服务器以实现统一多人跳棋游戏

javascript - 使用 Firebug 网络面板计时 : What is the onload time?

javascript - 为什么我的计时码总是返回0?

sql - Sql 触发器是同步的还是异步的?

go - 为什么 Goroutine 在 Busy Loop 之后还能被调度?