Skip to content

testing: benchmark is using low resolution time on Windows #31160

Description

@egonelbre

time.Now has a large resolution on Windows, so the benchmark results end up with a timing error.

As a demonstration here's code that measures QueryPerformanceCounter vs. time.Now: https://play.golang.org/p/FN4AtJ9b51m.

f:\Go\src\sandbox\benchmark>go version
go version devel +2bd767b102 Thu Mar 28 09:36:43 2019 +0000 windows/amd64

f:\Go\src\sandbox\benchmark>go run main.go
                             50%       90%       99%
time.Now    Z=9999935    1000000   1005100   1014600
QPC         Z=0              100       200       200

Which roughly says that my getting measurements at 1000000ns or 1ms granularity.

Fixing nanotime would be problematic (as seen in #8687), however benchmarks could use QueryPerformanceCounter instead.

Activity

  1. changed the title [-]testing: benchmark is using time.Now on Windows for measuring time.[/-] [+]testing: benchmark is using low resolution time on Windows for measuring time.[/+] on Mar 30, 2019
  2. egonelbre commented on Mar 30, 2019

    @egonelbre
    ContributorAuthor

    I could make an easy patch like I did in https://github.com/rakyll/hey/blob/master/requester/now_windows.go#L24. However since runtime already contains definitions for QPC was wondering whether it can be organized better.

  3. egonelbre commented on Mar 30, 2019

    @egonelbre
    ContributorAuthor

    As a more practical example:

    func Benchmark(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		fmt.Sprintf("hello world")
    	}
    }
    

    As we vary benchtime it shows quite a significant timing error:

    f:\Go\src\sandbox\benchmark>go test -bench . -benchtime 1000x .
    goos: windows
    goarch: amd64
    pkg: sandbox/benchmark
    Benchmark-32                1000                 0.00 ns/op
    PASS
    ok      sandbox/benchmark       0.261s
    
    f:\Go\src\sandbox\benchmark>go test -bench . -benchtime 100000x .
    goos: windows
    goarch: amd64
    pkg: sandbox/benchmark
    Benchmark-32              100000                80.4 ns/op
    PASS
    ok      sandbox/benchmark       0.285s
    
    f:\Go\src\sandbox\benchmark>go test -bench . -benchtime 1000000x .
    goos: windows
    goarch: amd64
    pkg: sandbox/benchmark
    Benchmark-32             1000000                67.0 ns/op
    PASS
    ok      sandbox/benchmark       0.342s
    
  4. changed the title [-]testing: benchmark is using low resolution time on Windows for measuring time.[/-] [+]testing: benchmark is using low resolution time on Windows[/+] on Mar 30, 2019
  5. networkimprov commented on Mar 31, 2019

    @networkimprov

    Related: #29714

  6. bcmills commented on Apr 12, 2019

    @bcmills
    Contributor
  7. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    TestingAn issue that has been verified to require only test changes, not just a test failure.
    on Apr 12, 2019
  8. added this to the Unplanned milestone on Apr 12, 2019
  9. egonelbre commented on Apr 17, 2019

    @egonelbre
    ContributorAuthor

    Ping.

    I could make a PR and discuss a specific fix, if that's requires less effort from core maintainers.

  10. josharian commented on Apr 17, 2019

    @josharian
    Contributor

    I don't have much to say about this. A higher resolution benchmarking timer certainly seems desirable. What are the downsides?

    cc @alexbrainman for windows

  11. egonelbre commented on Apr 17, 2019

    @egonelbre
    ContributorAuthor

    Some duplicated code for windows and handling for other platforms to be compatible with that.

    Specifically the qpc part

    // useQPCTime controls whether time.now and nanotime use QueryPerformanceCounter.
    . Unless there's a nice way to share that code with testing package.

  12. josharian commented on Apr 17, 2019

    @josharian
    Contributor

    We could use //go:linkname to get access to it. Or an internal package, perhaps.

  13. egonelbre commented on Apr 17, 2019

    @egonelbre
    ContributorAuthor

    After poking it a bit it seems using nanotimeQPC at the moment works only for Wine, since the implementation converts a division into multiplication that doesn't work in the general case.

    Usually the way to convert QPC value is result = qpcCounter * 1e9 / qpcFrequency. Currently the implementation uses qpcMultiplier = 1e9 / qpcFrequency and then result = qpcCounter * qpcMultiplier, which only works correctly when frequency is a power of 10.

    https://github.com/golang/go/blob/master/src/runtime/os_windows.go#L447

    Ideas so far:

    1. add QPC funcs into internal/syscall/windows and use them rather than try to modify runtime to fit the needs
    2. use division inside nanotimeQPC to avoid the error and accept the cost of division
    3. find a faster way to calculate division by qpcFrequency, if there exists one

    1 seems the smallest and easiest change. 2 requires adjusting things inside runtime... also, I'm not sure exactly how big the cost of additional division would be in practice. 3 seems slightly overkill, and I don't have a really good idea at the moment.

    Based on these thoughts, my instincts tell me to go with 1.

  14. 10 remaining items

  15. gopherbot commented on Jan 22, 2024

    @gopherbot
    Contributor

    Change https://go.dev/cl/557315 mentions this issue: testing: use QueryPerformanceCounter on Windows

  16. added
    NeedsFixThe path to resolution is known, but the work has not been done.
    FixPendingIssues that have a fix which has not yet been reviewed or submitted.
    and removed
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Apr 24, 2024
  17. modified the milestones: Unplanned, Go1.23 on Apr 24, 2024
  18. added a commit that references this issue on Apr 26, 2024
    1c47049
  19. alexbrainman commented on Apr 27, 2024

    @alexbrainman
    Member

    It appears the fix for this issue in CL 557315 broke all arm systems. I created #67077 to keep track of the breakage.

    Let me know if I should revert the CL.

    Thank you.

    Alex

  20. egonelbre commented on Apr 27, 2024

    @egonelbre
    ContributorAuthor

    I'm fine with reverting the CL to avoid blocking other work, but I don't think that's the underlying issue. If there's no immediate fix, then we can revert to get things building again. Anyways, let's continue the discussion in #67077.

  21. alexbrainman commented on Apr 27, 2024

    @alexbrainman
    Member

    I'm fine with reverting the CL to avoid blocking other work, but I don't think that's the underlying issue. If there's no immediate fix, then we can revert to get things building again.

    I agree. Let's wait for others before we create a revert. We cannot submit the revert anyways without Googlers.

    Anyways, let's continue the discussion in #67077.

    SGTM.

    Alex

  22. gopherbot commented on Apr 27, 2024

    @gopherbot
    Contributor

    Change https://go.dev/cl/582135 mentions this issue: testing: use time.Since in highPrecisionTime

  23. locked and limited conversation to collaborators on Apr 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixPendingIssues that have a fix which has not yet been reviewed or submitted.FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.PerformanceTestingAn issue that has been verified to require only test changes, not just a test failure.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions