Skip to content

Commit d864295

Browse files
committed
Time::HiRes.xs don't Win32's QueryPerformanceFrequency() over and over
-It is a boot time constant. It will not change without a motherboard or CPU swap and then rebooting. The actual 64 bit integer returned, reflects if the NT Kernel wants to use Intel's APIC Timer or Intel's 8253/8254 PIT Timer, or Intel's RDTSC instruction. NT Kernel will only use RDTSC backend if both the CPU and Northbridge swear upon a holy book, that they will fire an interrupt at every Intel/AMD SpeedSwitch/TurboBoost transition. The dynamic CPU speed correction factor logic lives inside the machine code of QueryPerformanceCounter(). Not inside QueryPerformanceFrequency() which has been part of MS;s frozen Public API since 1993. -the test: if (!QueryPerformanceFrequency(&l_tick_frequency)) croak("WT???"); can probably be removed one day, only Win2K or NT4 or Win95/98, running on any 32-bit CISC or 32-but RISC CPU arch, are capable of retval FALSE. The test is added out of paranoia. IDK what in real life on real HW can cause retval FALSE. -calc and save var unsigned __int64 qpc_res_ns; and unsigned __int64 qpc_res_ns_realtime; exactly once instead of re-calcing in the runloop, why not? HiRes.dll's .data section is only 0x650 bytes long and granularity is 0x1000/4096 bytes. -the BOOT: initialization code of the 3 true C static global vars, is written, to assume 2 ithreads, or 2 my_perl ptrs, or 2 different embbeding consumers of perl5XX.dll inside 1 OS process, can simultaneously call Dynaloader::bootstrap() or Time::HiRes::bootstrap() on 2 different CPU cores. This is unrealistic paranoia IMO, but CPU op lock xchg reg, [addr]; and mov [addr], reg; are both 7 bytes long. Maybe Windows >= 8.0 on ARM32/ARM64, want their memory fence/barrier formalities writing to an aligned 64 bit integer. So why not? -#define S_InterlockedExchange64(_d,_s) has S_ prefix, so no assumptions are made on MSVC and Mingw GCC, if InterlockedExchange64() is a macro or a symbol. Any age, any version, any build number, any FOSS project code owner, or any FOSS binary packager, of those 2 C compiler families.
1 parent 97c021c commit d864295

File tree

1 file changed

+35
-16
lines changed

1 file changed

+35
-16
lines changed

dist/Time-HiRes/HiRes.xs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,17 @@ typedef union {
135135
typedef struct {
136136
unsigned long run_count;
137137
unsigned __int64 base_ticks;
138-
unsigned __int64 tick_frequency;
139138
FT_t base_systime_as_filetime;
140139
unsigned __int64 reset_time;
141140
} my_cxt_t;
142141

142+
static unsigned __int64 tick_frequency = 0;
143+
static unsigned __int64 qpc_res_ns = 0;
144+
static unsigned __int64 qpc_res_ns_realtime = 0;
145+
146+
#define S_InterlockedExchange64(_d,_s) \
147+
InterlockedExchange64((LONG64 volatile *)(_d),(LONG64)(_s))
148+
143149
/* Visual C++ 2013 and older don't have the timespec structure.
144150
* Neither do mingw.org compilers with MinGW runtimes older than 3.22. */
145151
# if((defined(_MSC_VER) && _MSC_VER < 1900) || \
@@ -220,7 +226,6 @@ _GetSystemTimePreciseAsFileTime(pTHX_ FILETIME *out)
220226
if (MY_CXT.run_count++ == 0 ||
221227
MY_CXT.base_systime_as_filetime.ft_i64 > MY_CXT.reset_time) {
222228

223-
QueryPerformanceFrequency((LARGE_INTEGER*)&MY_CXT.tick_frequency);
224229
QueryPerformanceCounter((LARGE_INTEGER*)&MY_CXT.base_ticks);
225230
GetSystemTimeAsFileTime(&MY_CXT.base_systime_as_filetime.ft_val);
226231
ft.ft_i64 = MY_CXT.base_systime_as_filetime.ft_i64;
@@ -232,8 +237,8 @@ _GetSystemTimePreciseAsFileTime(pTHX_ FILETIME *out)
232237
QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
233238
ticks -= MY_CXT.base_ticks;
234239
ft.ft_i64 = MY_CXT.base_systime_as_filetime.ft_i64
235-
+ Const64(IV_1E7) * (ticks / MY_CXT.tick_frequency)
236-
+(Const64(IV_1E7) * (ticks % MY_CXT.tick_frequency)) / MY_CXT.tick_frequency;
240+
+ Const64(IV_1E7) * (ticks / tick_frequency)
241+
+(Const64(IV_1E7) * (ticks % tick_frequency)) / tick_frequency;
237242
diff = ft.ft_i64 - MY_CXT.base_systime_as_filetime.ft_i64;
238243
if (diff < -MAX_PERF_COUNTER_SKEW || diff > MAX_PERF_COUNTER_SKEW) {
239244
MY_CXT.base_ticks += ticks;
@@ -278,13 +283,12 @@ _clock_gettime(pTHX_ clockid_t clock_id, struct timespec *tp)
278283
break;
279284
}
280285
case CLOCK_MONOTONIC: {
281-
unsigned __int64 freq, ticks;
286+
unsigned __int64 ticks;
282287

283-
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
284288
QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
285289

286-
tp->tv_sec = (time_t)(ticks / freq);
287-
tp->tv_nsec = (long)((IV_1E9 * (ticks % freq)) / freq);
290+
tp->tv_sec = (time_t)(ticks / tick_frequency);
291+
tp->tv_nsec = (long)((IV_1E9 * (ticks % tick_frequency)) / tick_frequency);
288292
break;
289293
}
290294
default:
@@ -298,17 +302,10 @@ _clock_gettime(pTHX_ clockid_t clock_id, struct timespec *tp)
298302
static int
299303
_clock_getres(clockid_t clock_id, struct timespec *tp)
300304
{
301-
unsigned __int64 freq, qpc_res_ns;
302-
303-
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
304-
qpc_res_ns = IV_1E9 > freq ? IV_1E9 / freq : 1;
305-
306305
switch (clock_id) {
307306
case CLOCK_REALTIME:
308307
tp->tv_sec = 0;
309-
/* the resolution can't be smaller than 100ns because our implementation
310-
* of CLOCK_REALTIME is using FILETIME internally */
311-
tp->tv_nsec = (long)(qpc_res_ns > 100 ? qpc_res_ns : 100);
308+
tp->tv_nsec = (long)qpc_res_ns_realtime;
312309
break;
313310

314311
case CLOCK_MONOTONIC:
@@ -929,6 +926,28 @@ BOOT:
929926
#ifdef MY_CXT_KEY
930927
MY_CXT_INIT;
931928
#endif
929+
#if defined(WIN32) || defined(CYGWIN_WITH_W32API)
930+
if (tick_frequency == 0) { /* no DllMain() in very rare static Perls */
931+
unsigned __int64 l_tick_frequency;
932+
/* from MSDN: >= WinXP, function will always succeed and never return zero */
933+
if (!QueryPerformanceFrequency((LARGE_INTEGER*)&l_tick_frequency))
934+
croak("%s(): unimplemented in this platform", "QueryPerformanceFrequency");
935+
/* 32-bit CPU anti-sharding paranoia */
936+
S_InterlockedExchange64(&tick_frequency, l_tick_frequency);
937+
}
938+
if (qpc_res_ns == 0) {
939+
unsigned __int64 l_qpc_res_ns =
940+
IV_1E9 > tick_frequency ? IV_1E9 / tick_frequency : 1;
941+
S_InterlockedExchange64(&qpc_res_ns, l_qpc_res_ns);
942+
}
943+
if (qpc_res_ns_realtime == 0) {
944+
/* the resolution can't be smaller than 100ns because our implementation
945+
* of CLOCK_REALTIME is using FILETIME internally */
946+
unsigned __int64 l_qpc_res_ns_realtime =
947+
qpc_res_ns > 100 ? qpc_res_ns : 100;
948+
S_InterlockedExchange64(&qpc_res_ns_realtime, l_qpc_res_ns_realtime);
949+
}
950+
#endif
932951
#ifdef HAS_GETTIMEOFDAY
933952
{
934953
(void) hv_store(PL_modglobal, "Time::NVtime", 12,

0 commit comments

Comments
 (0)