douganmo1121 2017-09-02 08:49
浏览 59
已采纳

性能随机下降

I'm kind of a newbie in Go and there is something that confused me recently.

I have a piece of code (simplified version posted below) and I was trying to measure performanc for it. I did this in two ways: 1) a bencmark with testing package 2) manually logging time

Running the benchmark outputs a result

30000 55603 ns/op

which is fine, BUT... When I do the 30k runs of the same function logging the time for each iteration I get an output like this:

test took 0 ns test took 0 ns ... ~10 records all the same test took 1000100 ns test took 0 ns test took 0 ns ... lots of zeroes again test took 0 ns test took 1000000 ns test took 0 ns ...

Doing the math shows that the average is indeed 55603 ns/op just as the benchmark claims.

Ok, I said, I'm not that good in optimizing performance and not that into all the hardcore compiler stuff, but I guess that might be random garbage collection? So I turned on the gc log, made sure it shows some output, then turned off the gc for good aaand... no garbage collection, but I see the same picture - some iterations take a million times longer(?).

It is 99% that my understanding of all this is wrong somewhere, maybe someone can point me to the right direction or maybe someone knows for sure what the hell is going on? :)

P.S. Also, to me less that a nanosecond (0 ns) is somewhat surprising, that seems too fast, but the program does provide the result of computation, so I don't know what to think anymore. T_T

EDIT 1: Answering Kenny Grant's question: I was using goroutines to implement sort-of generator of values to have laziness, now I removed them and simplified the code. The issue is much less frequent now, but it is still reproducible. Playground link: https://play.golang.org/p/UQMgtT4Jrf Interesting thing is that does not happen on playground, but still happens on my machine.

EDIT 2: I'm running Go 1.9 on win7 x64

EDIT 3: Thanks to the responses I now know that this code cannot possible work properly on playground. I will repost the code snippet here so that we don't loose it. :)

type PrefType string
var types []PrefType = []PrefType{
    "TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5", "TYPE6",
}

func GetKeys(key string) []string {
    var result []string
    for _, t := range types {
        rr := doCalculations(t)
        for _, k := range rr {
            result = append(result, key + "." + k)
        }
    }
    return result
}

func doCalculations(prefType PrefType) []string {
    return []string{ string(prefType) + "something", string(prefType) + "else" }
}

func test() {
    start := time.Now()
    keysPrioritized := GetKeys("spec_key")
    for _, k := range keysPrioritized {
        _ = fmt.Sprint(k)
    }
    fmt.Printf("test took %v ns
", time.Since(start).Nanoseconds())
}

func main() {
    for i := 0; i < 30000; i++  {
        test()
    }
}

Here is the output on my machine:

enter image description here

EDIT 4: I have tried the same on my laptop with Ubuntu 17.04, the output is reasonable, no zeroes and millions. Seems like a Windows-specific issue in the compiler/runtime lib. Would be great if someone can verify this on their machine (Win 7/8/10).

  • 写回答

2条回答 默认 最新

  • douzhi8488 2017-09-02 15:06
    关注

    On Windows, for such a tiny duration, you don't have precise enough time stamps. Linux has more precise time stamps. By design, Go benchmarks run for at least one second. Go1.9+ uses the monotonic (m) value to compute the duration.

    On Windows:

    timedur.go:

    package main
    
    import (
        "fmt"
        "os"
        "time"
    )
    
    type PrefType string
    
    var types []PrefType = []PrefType{
        "TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5", "TYPE6",
    }
    
    func GetKeys(key string) []string {
        var result []string
        for _, t := range types {
            rr := doCalculations(t)
            for _, k := range rr {
                result = append(result, key+"."+k)
            }
        }
        return result
    }
    
    func doCalculations(prefType PrefType) []string {
        return []string{string(prefType) + "something", string(prefType) + "else"}
    }
    
    func test() {
        start := time.Now()
        keysPrioritized := GetKeys("spec_key")
        for _, k := range keysPrioritized {
            _ = fmt.Sprint(k)
        }
        end := time.Now()
        fmt.Printf("test took %v ns
    ", time.Since(start).Nanoseconds())
        fmt.Println(start)
        fmt.Println(end)
        if end.Sub(start) < time.Microsecond {
            os.Exit(1)
        }
    }
    
    func main() {
        for i := 0; i < 30000; i++ {
            test()
        }
    }
    

    Output:

    >go run timedur.go
    test took 1026000 ns
    2017-09-02 14:21:58.1488675 -0700 PDT m=+0.010003700
    2017-09-02 14:21:58.1498935 -0700 PDT m=+0.011029700
    test took 0 ns
    2017-09-02 14:21:58.1538658 -0700 PDT m=+0.015002000
    2017-09-02 14:21:58.1538658 -0700 PDT m=+0.015002000
    exit status 1
    >
    

    On Linux:

    Output:

    $ go run timedur.go
    test took 113641 ns
    2017-09-02 14:52:02.917175333 +0000 UTC m=+0.001041249
    2017-09-02 14:52:02.917287569 +0000 UTC m=+0.001153717
    test took 23614 ns
    2017-09-02 14:52:02.917600301 +0000 UTC m=+0.001466208
    2017-09-02 14:52:02.917623585 +0000 UTC m=+0.001489354
    test took 22814 ns
    2017-09-02 14:52:02.917726364 +0000 UTC m=+0.001592236
    2017-09-02 14:52:02.917748805 +0000 UTC m=+0.001614575
    test took 21139 ns
    2017-09-02 14:52:02.917818409 +0000 UTC m=+0.001684292
    2017-09-02 14:52:02.917839184 +0000 UTC m=+0.001704954
    test took 21478 ns
    2017-09-02 14:52:02.917911899 +0000 UTC m=+0.001777712
    2017-09-02 14:52:02.917932944 +0000 UTC m=+0.001798712
    test took 31032 ns
    <SNIP>
    

    The results are comparable. They were run on the same machine, a dual-boot with Windows 10 and Ubuntu 16.04.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误
  • ¥15 一道python难题3