VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3-timing-1-32.c32@ 93603

Last change on this file since 93603 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette