1 | /* $Id: bs3-timing-1-32.c32 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * BS3Kit - bs3-timinig-1, 32-bit C code.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2007-2023 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * The contents of this file may alternatively be used under the terms
|
---|
26 | * of the Common Development and Distribution License Version 1.0
|
---|
27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
29 | * CDDL are applicable instead of those of the GPL.
|
---|
30 | *
|
---|
31 | * You may elect to license modified versions of this file under the
|
---|
32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
33 | *
|
---|
34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
35 | */
|
---|
36 |
|
---|
37 |
|
---|
38 | /*********************************************************************************************************************************
|
---|
39 | * Header Files *
|
---|
40 | *********************************************************************************************************************************/
|
---|
41 | #ifndef STANDALONE_EXECUTABLE
|
---|
42 | # include <bs3kit.h>
|
---|
43 | #endif
|
---|
44 | #include <iprt/asm-amd64-x86.h>
|
---|
45 | #include <iprt/asm-math.h>
|
---|
46 | #include <iprt/asm.h>
|
---|
47 | #include <iprt/uint128.h>
|
---|
48 |
|
---|
49 |
|
---|
50 | /*********************************************************************************************************************************
|
---|
51 | * Structures and Typedefs *
|
---|
52 | *********************************************************************************************************************************/
|
---|
53 | /** TSC timing results. */
|
---|
54 | typedef struct BS3TIMING1RESULT
|
---|
55 | {
|
---|
56 | /** Number of nanoseconds elapsed while testing. */
|
---|
57 | uint64_t cNsElapsed;
|
---|
58 | /** Number of CPU ticks elapsed while testing. */
|
---|
59 | uint64_t cTicksElapsed;
|
---|
60 | /** The minium number of ticks between TSC reads. */
|
---|
61 | uint64_t cTicksMin;
|
---|
62 | /** The maximum number of ticks between TSC reads. */
|
---|
63 | uint64_t cTicksMax;
|
---|
64 | /** The sum of all TSC read deltas. */
|
---|
65 | uint64_t cTicksSum;
|
---|
66 | /** Number of loops (TSC read deltas). */
|
---|
67 | uint64_t cTotalLoops;
|
---|
68 | /** Number of times TSC moved backwards. */
|
---|
69 | uint64_t cBackwards;
|
---|
70 | /** Approx log2(cTicks) distribution. */
|
---|
71 | uint64_t aDistribution[65];
|
---|
72 | } BS3TIMING1RESULT;
|
---|
73 |
|
---|
74 |
|
---|
75 | /*********************************************************************************************************************************
|
---|
76 | * Global Variables *
|
---|
77 | *********************************************************************************************************************************/
|
---|
78 | /** The total result. */
|
---|
79 | static BS3TIMING1RESULT g_TotalResult;
|
---|
80 |
|
---|
81 | /** Set if history wrapped (i.e. table is full). */
|
---|
82 | static bool g_fBigHistoryWrapped = false;
|
---|
83 | /** The next history entry. */
|
---|
84 | static uint32_t g_iBigHistory;
|
---|
85 | /** History of large gaps. */
|
---|
86 | static struct { uint64_t uTsc, cTicksDelta; } g_aBigHistory[384];
|
---|
87 |
|
---|
88 |
|
---|
89 |
|
---|
90 | /**
|
---|
91 | * Pretty prints
|
---|
92 | */
|
---|
93 | static void bs3Timing1_PrintTicks(uint64_t cTicks, uint64_t uCpuFreq)
|
---|
94 | {
|
---|
95 | if (uCpuFreq > _128M)
|
---|
96 | {
|
---|
97 | if (cTicks >= uCpuFreq * 1000)
|
---|
98 | Bs3TestPrintf("%'RU64s", cTicks / uCpuFreq);
|
---|
99 | else
|
---|
100 | {
|
---|
101 | const char *pszUnit;
|
---|
102 | uint64_t uValue;
|
---|
103 | if (cTicks >= uCpuFreq)
|
---|
104 | {
|
---|
105 | pszUnit = "s";
|
---|
106 | uValue = (cTicks * RT_MS_1SEC) / uCpuFreq;
|
---|
107 | }
|
---|
108 | else if (cTicks * RT_MS_1SEC >= uCpuFreq)
|
---|
109 | {
|
---|
110 | pszUnit = "ms";
|
---|
111 | uValue = (cTicks * RT_US_1SEC) / uCpuFreq;
|
---|
112 | }
|
---|
113 | else if (cTicks * RT_US_1SEC >= uCpuFreq)
|
---|
114 | {
|
---|
115 | pszUnit = "us";
|
---|
116 | uValue = (cTicks * RT_NS_1SEC) / uCpuFreq;
|
---|
117 | }
|
---|
118 | else if (cTicks * RT_NS_1SEC >= uCpuFreq)
|
---|
119 | {
|
---|
120 | pszUnit = "ns";
|
---|
121 | uValue = (cTicks * UINT64_C(1000000000000)) / uCpuFreq;
|
---|
122 | }
|
---|
123 | else
|
---|
124 | {
|
---|
125 | Bs3TestPrintf("%'RU64ps", (cTicks * UINT64_C(1000000000000)) / uCpuFreq);
|
---|
126 | return;
|
---|
127 | }
|
---|
128 | Bs3TestPrintf("%u.%03u%s (%'RU64 ticks)", (uint32_t)uValue / 1000, (uint32_t)uValue % 1000, pszUnit, cTicks);
|
---|
129 | }
|
---|
130 | }
|
---|
131 | else
|
---|
132 | Bs3TestPrintf("%'RU64 ticks", cTicks);
|
---|
133 | }
|
---|
134 |
|
---|
135 |
|
---|
136 | /**
|
---|
137 | * Prints a result.
|
---|
138 | *
|
---|
139 | * @param pResult The result to print.
|
---|
140 | * @param iRun The run (loop in qpc parlance).
|
---|
141 | * @param uVerbosity The verbosity level.
|
---|
142 | */
|
---|
143 | static void bs3Timing1_PrintResult(BS3TIMING1RESULT const *pResult, unsigned iRun, unsigned uVerbosity)
|
---|
144 | {
|
---|
145 | uint64_t uCpuFreq;
|
---|
146 |
|
---|
147 | /*
|
---|
148 | * Calc CPU frequency.
|
---|
149 | */
|
---|
150 | if (pResult->cNsElapsed > 0 && pResult->cTicksElapsed > 0)
|
---|
151 | {
|
---|
152 | #if 1
|
---|
153 | RTUINT128U Tmp1, Divisor, Result;
|
---|
154 | RTUInt128Div(&Result,
|
---|
155 | RTUInt128MulU64ByU64(&Tmp1, pResult->cTicksElapsed, RT_NS_1SEC),
|
---|
156 | RTUInt128AssignU64(&Divisor, pResult->cNsElapsed));
|
---|
157 | uCpuFreq = Result.s.Lo;
|
---|
158 | #else
|
---|
159 | unsigned const cShift = pResult->cTicksElapsed < UINT64_C(0x000225C17D04) ? 0
|
---|
160 | : pResult->cTicksElapsed < UINT64_C(0x00225C17D04D) ? 4
|
---|
161 | : pResult->cTicksElapsed < UINT64_C(0x0225C17D04DA) ? 8
|
---|
162 | : pResult->cTicksElapsed < UINT64_C(0x225C1D940BF6) ? 12
|
---|
163 | : 16;
|
---|
164 | uCpuFreq = pResult->cTicksElapsed * ((uint64_t)RT_NS_1SEC >> cShift) / (pResult->cNsElapsed >> cShift);
|
---|
165 | #endif
|
---|
166 | }
|
---|
167 | else
|
---|
168 | uCpuFreq = 1;
|
---|
169 |
|
---|
170 | /*
|
---|
171 | * Report results.
|
---|
172 | *
|
---|
173 | * Note! in 32-bit and 16-bit mode, values 4G or higher gets formatted as
|
---|
174 | * hexadecimal to avoid 64-bit division.
|
---|
175 | */
|
---|
176 | Bs3TestPrintf("Loop #%u: %'RU64 tests: ", iRun, pResult->cTotalLoops);
|
---|
177 |
|
---|
178 | Bs3TestPrintf("average ");
|
---|
179 | bs3Timing1_PrintTicks(pResult->cTicksSum / pResult->cTotalLoops, uCpuFreq);
|
---|
180 | Bs3TestPrintf(", min ");
|
---|
181 | bs3Timing1_PrintTicks(pResult->cTicksMin, uCpuFreq);
|
---|
182 | Bs3TestPrintf(", max ");
|
---|
183 | bs3Timing1_PrintTicks(pResult->cTicksMax, uCpuFreq);
|
---|
184 | Bs3TestPrintf("\n");
|
---|
185 |
|
---|
186 | /* Distribution (tick delta log2-ish). */
|
---|
187 | if (uVerbosity > 0)
|
---|
188 | {
|
---|
189 | unsigned iItem = 0;
|
---|
190 | unsigned i;
|
---|
191 | for (i = uVerbosity > 1 ? 0 : 5; i < RT_ELEMENTS(pResult->aDistribution); i++)
|
---|
192 | if (pResult->aDistribution[i] != 0)
|
---|
193 | {
|
---|
194 | if (iItem >= 6)
|
---|
195 | {
|
---|
196 | iItem = 0;
|
---|
197 | Bs3TestPrintf("\n");
|
---|
198 | }
|
---|
199 | iItem++;
|
---|
200 | Bs3TestPrintf(" %'11RU64|2^%-2u", pResult->aDistribution[i], i);
|
---|
201 | }
|
---|
202 | if (uVerbosity > 1)
|
---|
203 | Bs3TestPrintf(iItem < 6 ? " (%'RU64 Hz)\n" : "\n (%'RU64 Hz)\n", uCpuFreq);
|
---|
204 | else
|
---|
205 | Bs3TestPrintf("\n");
|
---|
206 | }
|
---|
207 | if (pResult->cBackwards != 0)
|
---|
208 | Bs3TestFailedF("TSC went backwards %'RU64 time(s)", pResult->cBackwards);
|
---|
209 | }
|
---|
210 |
|
---|
211 |
|
---|
212 | /**
|
---|
213 | * Do one TSC timing iteration.
|
---|
214 | *
|
---|
215 | * @param iRun The iteration number (loop).
|
---|
216 | * @param cSecs The number of seconds to sample TSCs.
|
---|
217 | * @param uVerbosity The noise level.
|
---|
218 | * @param iMinHistory The threshold level to put stuff in g_auTscHistory.
|
---|
219 | */
|
---|
220 | static void bs3Timing1_Tsc_One(unsigned iRun, uint32_t cSecs, unsigned uVerbosity, unsigned iMinHistory)
|
---|
221 | {
|
---|
222 | uint64_t const nsStart = Bs3TestNow();
|
---|
223 | uint64_t const uTscStart = ASMReadTSC();
|
---|
224 | uint64_t const nsDeadline = nsStart + cSecs * RT_NS_1SEC_64;
|
---|
225 | uint64_t cNsElapsed;
|
---|
226 | BS3TIMING1RESULT Result;
|
---|
227 | unsigned i;
|
---|
228 |
|
---|
229 | Bs3MemZero(&Result, sizeof(Result));
|
---|
230 | Result.cTicksMin = UINT64_MAX;
|
---|
231 |
|
---|
232 | /*
|
---|
233 | * Test loop.
|
---|
234 | */
|
---|
235 | do
|
---|
236 | {
|
---|
237 | unsigned cLoops = 100000 + 1;
|
---|
238 | Result.cTotalLoops += cLoops - 1;
|
---|
239 | while (--cLoops != 0)
|
---|
240 | {
|
---|
241 | uint64_t uTscPrev = ASMReadTSC();
|
---|
242 | uint64_t uTscNow = ASMReadTSC();
|
---|
243 | uint64_t cTicks = uTscNow - uTscPrev;
|
---|
244 | unsigned iBit;
|
---|
245 |
|
---|
246 | /* check that it doesn't go backwards*/
|
---|
247 | if ((int64_t)cTicks < 0)
|
---|
248 | Result.cBackwards++;
|
---|
249 |
|
---|
250 | /* min/max/avg */
|
---|
251 | Result.cTicksSum += cTicks;
|
---|
252 | if (cTicks < Result.cTicksMin)
|
---|
253 | Result.cTicksMin = cTicks;
|
---|
254 | if (cTicks > Result.cTicksMax)
|
---|
255 | Result.cTicksMax = cTicks;
|
---|
256 |
|
---|
257 | /* result distribution by most significant bit. */
|
---|
258 | iBit = ASMBitLastSetU64(cTicks);
|
---|
259 | Result.aDistribution[iBit] += 1;
|
---|
260 | if (iBit < iMinHistory)
|
---|
261 | { /* likely */ }
|
---|
262 | else
|
---|
263 | {
|
---|
264 | g_aBigHistory[g_iBigHistory].uTsc = uTscPrev;
|
---|
265 | g_aBigHistory[g_iBigHistory].cTicksDelta = cTicks;
|
---|
266 | if (++g_iBigHistory >= RT_ELEMENTS(g_aBigHistory))
|
---|
267 | {
|
---|
268 | g_iBigHistory = 0;
|
---|
269 | g_fBigHistoryWrapped = true;
|
---|
270 | }
|
---|
271 | }
|
---|
272 | }
|
---|
273 | } while ((cNsElapsed = Bs3TestNow()) < nsDeadline);
|
---|
274 |
|
---|
275 | Result.cTicksElapsed = ASMReadTSC() - uTscStart;
|
---|
276 | Result.cNsElapsed = cNsElapsed - nsStart;
|
---|
277 |
|
---|
278 | bs3Timing1_PrintResult(&Result, iRun, uVerbosity);
|
---|
279 |
|
---|
280 | /* Add to total. */
|
---|
281 | g_TotalResult.cNsElapsed += Result.cNsElapsed;
|
---|
282 | g_TotalResult.cTicksElapsed += Result.cTicksElapsed;
|
---|
283 | if (Result.cTicksMin < g_TotalResult.cTicksMin || g_TotalResult.cTicksMin == 0)
|
---|
284 | g_TotalResult.cTicksMin = Result.cTicksMin;
|
---|
285 | if (Result.cTicksMax > g_TotalResult.cTicksMax)
|
---|
286 | g_TotalResult.cTicksMax += Result.cTicksMax;
|
---|
287 | g_TotalResult.cTicksSum += Result.cTicksSum;
|
---|
288 | g_TotalResult.cTotalLoops += Result.cTotalLoops;
|
---|
289 | g_TotalResult.cBackwards += Result.cBackwards;
|
---|
290 | for (i = 0; i < RT_ELEMENTS(Result.aDistribution); i++)
|
---|
291 | g_TotalResult.aDistribution[i] += Result.aDistribution[i];
|
---|
292 | }
|
---|
293 |
|
---|
294 |
|
---|
295 | /**
|
---|
296 | * The TSC test driver.
|
---|
297 | *
|
---|
298 | * @param cLoops Number of test iterations.
|
---|
299 | * @param cSecs The number of seconds per iteration.
|
---|
300 | * @param uVerbosity How noisy we should be.
|
---|
301 | * @param iMinHistory The threshold for big gap history.
|
---|
302 | */
|
---|
303 | static void bs3Timing1_Tsc_Driver(unsigned cLoops, unsigned cSecs, unsigned uVerbosity, unsigned iMinHistory)
|
---|
304 | {
|
---|
305 | unsigned iLoop;
|
---|
306 |
|
---|
307 | #if 1
|
---|
308 | /*
|
---|
309 | * Verify that the first/last bit in U64 works (didn't).
|
---|
310 | */
|
---|
311 | iLoop = ASMBitLastSetU64( UINT64_C(0x1000100010001000)); if (iLoop != 61) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
|
---|
312 | iLoop = ASMBitFirstSetU64(UINT64_C(0x1000100010001000)); if (iLoop != 13) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
|
---|
313 | iLoop = ASMBitLastSetU64( UINT64_C(0x000ffff000000000)); if (iLoop != 52) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
|
---|
314 | iLoop = ASMBitFirstSetU64(UINT64_C(0x000ffff000000000)); if (iLoop != 37) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
|
---|
315 | #endif
|
---|
316 |
|
---|
317 | /*
|
---|
318 | * Do the work.
|
---|
319 | */
|
---|
320 | Bs3TestPrintf("Running %u loops, %u second%s each...\n", cLoops, cSecs, cSecs != 1 ? "s" : "");
|
---|
321 | for (iLoop = 1; iLoop <= cLoops; iLoop++)
|
---|
322 | bs3Timing1_Tsc_One(iLoop, cSecs, uVerbosity, iMinHistory);
|
---|
323 |
|
---|
324 | /*
|
---|
325 | * Report the total.
|
---|
326 | */
|
---|
327 | Bs3TestPrintf("Total:\n");
|
---|
328 | bs3Timing1_PrintResult(&g_TotalResult, iLoop, uVerbosity + 1);
|
---|
329 |
|
---|
330 | /*
|
---|
331 | * Dump the large gap history, if any.
|
---|
332 | */
|
---|
333 | if (g_fBigHistoryWrapped || g_iBigHistory > 0)
|
---|
334 | {
|
---|
335 | uint32_t const iFirst = g_fBigHistoryWrapped ? g_iBigHistory : 0;
|
---|
336 | uint32_t const iEnd = g_iBigHistory;
|
---|
337 | uint64_t uTscPrev = g_aBigHistory[iFirst].uTsc;
|
---|
338 | uint32_t i = iFirst;
|
---|
339 | Bs3TestPrintf("Big gap history (TSC, prev delta, test delta|level):\n");
|
---|
340 | do
|
---|
341 | {
|
---|
342 | Bs3TestPrintf(" %'RU64: %'14RU64 - %'14RU64|%u\n", g_aBigHistory[i].uTsc, g_aBigHistory[i].uTsc - uTscPrev,
|
---|
343 | g_aBigHistory[i].cTicksDelta, ASMBitLastSetU64(g_aBigHistory[i].cTicksDelta));
|
---|
344 | uTscPrev = g_aBigHistory[i].uTsc;
|
---|
345 | if (++i >= RT_ELEMENTS(g_aBigHistory))
|
---|
346 | i = 0;
|
---|
347 | } while (i != iEnd);
|
---|
348 | }
|
---|
349 | else
|
---|
350 | Bs3TestPrintf("No big gap history.\n");
|
---|
351 | }
|
---|
352 |
|
---|
353 |
|
---|
354 | #ifndef STANDALONE_EXECUTABLE
|
---|
355 | BS3_DECL(void) bs3Timing1_Tsc_pe32(void)
|
---|
356 | {
|
---|
357 | Bs3TestPrintf("bs3Timing1_Tsc_pe32\n");
|
---|
358 | bs3Timing1_Tsc_Driver(60, 10 /*sec*/, 1 /*uVerbosity*/, 17);
|
---|
359 | }
|
---|
360 | #endif
|
---|
361 |
|
---|
362 | /* P.S. don't forget: VBoxManage setextradata bs3-timing-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1 */
|
---|
363 |
|
---|