1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #include "stdafx.h" |
6 | |
7 | #include "cycletimer.h" |
8 | #include "winbase.h" |
9 | #include "winwrap.h" |
10 | #include "assert.h" |
11 | #include "utilcode.h" |
12 | |
13 | bool CycleTimer::GetThreadCyclesS(unsigned __int64* cycles) |
14 | { |
15 | BOOL res = FALSE; |
16 | res = QueryThreadCycleTime(GetCurrentThread(), cycles); |
17 | return res != FALSE; |
18 | } |
19 | |
20 | static const int SampleLoopSize = 1000000; |
21 | |
22 | // static |
23 | double CycleTimer::CyclesPerSecond() |
24 | { |
25 | // Windows does not provide a way of converting cycles to time -- reasonably enough, |
26 | // since the frequency of a machine may vary, due, e.g., to power management. |
27 | // Windows *does* allow you to translate QueryPerformanceCounter counts into time, |
28 | // however. So we'll assume that the clock speed stayed constant, and measure both the |
29 | // QPC counts and cycles of a short loop, to get a conversion factor. |
30 | LARGE_INTEGER lpFrequency; |
31 | if (!QueryPerformanceFrequency(&lpFrequency)) return 0.0; |
32 | // Otherwise... |
33 | LARGE_INTEGER qpcStart; |
34 | unsigned __int64 cycleStart; |
35 | if (!QueryPerformanceCounter(&qpcStart)) return 0.0; |
36 | if (!GetThreadCyclesS(&cycleStart)) return 0.0; |
37 | volatile int sum = 0; |
38 | for (int k = 0; k < SampleLoopSize; k++) |
39 | { |
40 | sum += k; |
41 | } |
42 | LARGE_INTEGER qpcEnd; |
43 | if (!QueryPerformanceCounter(&qpcEnd)) return 0.0; |
44 | unsigned __int64 cycleEnd; |
45 | if (!GetThreadCyclesS(&cycleEnd)) return 0.0; |
46 | |
47 | double qpcTicks = ((double)qpcEnd.QuadPart) - ((double)qpcStart.QuadPart); |
48 | double secs = (qpcTicks / ((double)lpFrequency.QuadPart)); |
49 | double cycles = ((double)cycleEnd) - ((double)cycleStart); |
50 | return cycles / secs; |
51 | } |
52 | |
53 | // static |
54 | unsigned __int64 CycleTimer::QueryOverhead() |
55 | { |
56 | unsigned __int64 tot = 0; |
57 | unsigned __int64 startCycles; |
58 | unsigned __int64 endCycles; |
59 | const int N = 1000; |
60 | bool b = GetThreadCyclesS(&startCycles); assert(b); |
61 | for (int i = 0; i < N; i++) |
62 | { |
63 | b = GetThreadCyclesS(&endCycles); assert(b); |
64 | tot += (endCycles-startCycles); |
65 | startCycles = endCycles; |
66 | } |
67 | return tot/N; |
68 | } |
69 | |
70 | // static |
71 | void CycleTimer::InterlockedAddU64(unsigned __int64* loc, unsigned __int64 amount) |
72 | { |
73 | volatile __int64* vloc = (volatile __int64*)loc; |
74 | unsigned __int64 prev = *vloc; |
75 | for (;;) |
76 | { |
77 | unsigned __int64 next = prev + amount; |
78 | __int64 snext = (__int64)next; |
79 | __int64 sprev = (__int64)prev; |
80 | __int64 res = InterlockedCompareExchange64(vloc, snext, sprev); |
81 | if (res == sprev) return; |
82 | else prev = (unsigned __int64)res; |
83 | } |
84 | } |
85 | |
86 | |