Skip to content

Commit 26e9c3f

Browse files
committed
Time::HiRes Win32 add nv_gettimeofday() nv_clock_gettime() anti-precsn loss
-add NV retval variants nv_gettimeofday() and nv_clock_gettime(clock_id, &status), the splitting of the solo U64, into 2 IVs/UVs (64b IVs/UVs on my system), then recombing those 2 integers with integer or FP double logic, was very messy and verbose machine code and no, MSVC didn't "algebra" const fold away the splitting and recombing logic, so just create polyfills that always return NVs from the start -do "- ((U64)EPOCH_BIAS" with U64 logic, for maximum chance of no rounding/no precision loss, then do division with FP logic for maximum fractional number precision -"NV nv = nv_clock_gettime(clock_id, &status);" is inlined away, var bool status; has no C stack or register representation in mach code with MSVC 2022 -O1. Returning a pass by copy struct {NV nv; bool success;}; was considered, but never tried, b/c of Win64 AMD64 ABI's "rule" of all retval types > 8 bytes become secret ptrs and a secret 1st arg. Maybe MSVC would inline and fold away the struct, maybe it would not. I didn't try it. Current impl is working as intended. -nv_clock_gettime() still needs to reject junk values in clock_id remember -add tick_frequency_nv, so U64 -> NV is done 1x at startup, not in the run loop -S_croak_xs_unimplemented(const CV *const cv) silence CC warning, cv_name() doesn't want a const CV* head struct
1 parent 11ac8fc commit 26e9c3f

File tree

1 file changed

+96
-9
lines changed

1 file changed

+96
-9
lines changed

dist/Time-HiRes/HiRes.xs

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ typedef int clockid_t;
246246
# define HAS_GETTIMEOFDAY
247247
# endif
248248

249+
# ifndef HAS_NV_GETTIMEOFDAY
250+
# define HAS_NV_GETTIMEOFDAY
251+
# endif
252+
249253
/* shows up in winsock.h?
250254
struct timeval {
251255
long tv_sec;
@@ -270,6 +274,7 @@ typedef struct {
270274
typedef BOOL (WINAPI *pfnQueryPerformanceCounter_T)(LARGE_INTEGER*);
271275

272276
static unsigned __int64 tick_frequency = 0;
277+
static NV tick_frequency_nv = 0;
273278
static unsigned __int64 qpc_res_ns = 0;
274279
static unsigned __int64 qpc_res_ns_realtime = 0;
275280
static pfnQueryPerformanceCounter_T pfnQueryPerformanceCounter = NULL;
@@ -331,6 +336,10 @@ START_MY_CXT
331336
# undef clock_gettime
332337
# define clock_gettime(clock_id, tp) _clock_gettime(aTHX_ clock_id, tp)
333338

339+
# define TIME_HIRES_NV_CLOCK_GETTIME
340+
# undef nv_clock_gettime
341+
# define nv_clock_gettime(clock_id, _bp) _nv_clock_gettime(aTHX_ clock_id, _bp)
342+
334343
# undef clock_getres
335344
# define clock_getres(clock_id, tp) _clock_getres(clock_id, tp)
336345

@@ -447,6 +456,18 @@ _gettimeofday_x(pTHX)
447456
return tp;
448457
}
449458

459+
PERL_STATIC_FORCE_INLINE NV
460+
nv_gettimeofday_x(pTHX)
461+
{
462+
FT_t ft;
463+
464+
GetSystemTimePreciseAsFileTime(&ft.ft_val);
465+
466+
/* FP seconds since epoch */
467+
return ((NV)((U64)((U64)ft.ft_i64) - ((U64)EPOCH_BIAS))) / ((NV)NV_1E7);
468+
}
469+
#define nv_gettimeofday() nv_gettimeofday_x(aTHX)
470+
450471
/* force inline it, because XS_Time__HiRes_clock_gettime() is the only caller */
451472

452473
PERL_STATIC_FORCE_INLINE int
@@ -477,6 +498,30 @@ _clock_gettime(pTHX_ clockid_t clock_id, struct timespec *tp)
477498
return 0;
478499
}
479500

501+
PERL_STATIC_FORCE_INLINE NV
502+
_nv_clock_gettime(pTHX_ clockid_t clock_id, bool * statusp)
503+
{
504+
FT_t ft;
505+
unsigned __int64 ticks;
506+
unsigned __int64 time_sys;
507+
508+
*statusp = 0;
509+
switch (clock_id) {
510+
case CLOCK_REALTIME:
511+
GetSystemTimePreciseAsFileTime(&ft.ft_val);
512+
time_sys = ft.ft_i64;
513+
return ((NV)((U64)((U64)time_sys) - ((U64)EPOCH_BIAS))) / ((NV)NV_1E7);
514+
case CLOCK_MONOTONIC:
515+
QueryPerformanceCounter((LARGE_INTEGER*)&ft.ft_i64);
516+
ticks = ft.ft_i64;
517+
return ((NV)ticks) / tick_frequency_nv;
518+
default:
519+
*statusp = 1;
520+
errno = EINVAL;
521+
return -1.0;
522+
}
523+
}
524+
480525
static int
481526
_clock_getres(clockid_t clock_id, struct timespec *tp)
482527
{
@@ -807,21 +852,29 @@ myNVtime()
807852
return myNVtime_cxt(aTHX);
808853
# endif
809854
# endif
855+
#ifdef HAS_NV_GETTIMEOFDAY
856+
return nv_gettimeofday();
857+
#else
810858
struct timeval Tp;
811859
int status;
812860
status = gettimeofday (&Tp, NULL);
813861
return status == 0 ? Tp.tv_sec + (Tp.tv_usec / NV_1E6) : -1.0;
862+
#endif
814863
}
815864

816865
#ifdef PERL_IMPLICIT_CONTEXT
817866

818867
static NV
819868
myNVtime_cxt(pTHX)
820869
{
870+
#ifdef HAS_NV_GETTIMEOFDAY
871+
return nv_gettimeofday();
872+
#else
821873
struct timeval Tp;
822874
int status;
823875
status = gettimeofday (&Tp, NULL);
824876
return status == 0 ? Tp.tv_sec + (Tp.tv_usec / NV_1E6) : -1.0;
877+
#endif
825878
}
826879

827880
#endif
@@ -1116,10 +1169,10 @@ nsec_without_unslept(struct timespec *sleepfor,
11161169
#endif
11171170

11181171
static void
1119-
S_croak_xs_unimplemented(const CV *const cv);
1172+
S_croak_xs_unimplemented(CV *const cv);
11201173

11211174
static void
1122-
S_croak_xs_unimplemented(const CV *const cv)
1175+
S_croak_xs_unimplemented(CV *const cv)
11231176
{
11241177
dTHX;
11251178
SV* sv = cv_name(cv, NULL, 0);
@@ -1175,6 +1228,7 @@ BOOT:
11751228
"QueryPerformanceFrequency");
11761229
l_tick_frequency = l_tick_frequency_mem;
11771230
/* 32-bit CPU anti-sharding paranoia */
1231+
tick_frequency_nv = (NV)l_tick_frequency;
11781232
S_InterlockedExchange64(&tick_frequency, l_tick_frequency);
11791233
}
11801234
l_qpc_res_ns = qpc_res_ns;
@@ -1509,30 +1563,47 @@ alarm(seconds,interval=0)
15091563

15101564
#ifdef HAS_GETTIMEOFDAY
15111565

1566+
#ifdef HAS_NV_GETTIMEOFDAY
1567+
# define HAS_NV_GETTIMEOFDAY_BOOL 1
1568+
#else
1569+
# define HAS_NV_GETTIMEOFDAY_BOOL 0
1570+
#endif
1571+
15121572
void
15131573
gettimeofday()
15141574
PREINIT:
15151575
struct timeval Tp;
15161576
int status;
15171577
OP* const op = PL_op;
15181578
U8 is_G_LIST = GIMME_V == G_LIST;
1579+
NV nv;
1580+
const U8 do_taint = 1;
15191581
PPCODE:
15201582
if (is_G_LIST)
15211583
EXTEND(sp, 2);
1584+
else if(HAS_NV_GETTIMEOFDAY_BOOL) {
1585+
#ifdef HAS_NV_GETTIMEOFDAY
1586+
nv = nv_gettimeofday();
1587+
#endif
1588+
goto ret_1_nv;
1589+
}
15221590
status = gettimeofday (&Tp, NULL);
15231591
if (status == 0) {
1524-
if (is_G_LIST) { /* copy to registers to prove sv_2mortal/newSViv */
1592+
if (HAS_NV_GETTIMEOFDAY_BOOL || is_G_LIST) {
1593+
/* copy to registers to prove sv_2mortal/newSViv */
15251594
IV sec = Tp.tv_sec; /* can't modify the values */
15261595
IV usec = Tp.tv_usec;
15271596
PUSHs(sv_2mortal(newSViv(sec)));
15281597
PUSHs(sv_2mortal(newSViv(usec)));
15291598
} else {
15301599
/* no Perl_leave_adjust_stacks() hazard here,
15311600
only a PP vs call_sv() hazard */
1532-
NV nv = Tp.tv_sec + (Tp.tv_usec / NV_1E6);
1533-
const U8 do_taint = 1;
1534-
NV TARGn_nv = nv;
1601+
NV TARGn_nv;
15351602
SV* rsv;
1603+
nv = Tp.tv_sec + (Tp.tv_usec / NV_1E6);
1604+
1605+
ret_1_nv:
1606+
TARGn_nv = nv;
15361607
if (op->op_private & OPpENTERSUB_HASTARG) {
15371608
rsv = PAD_SV(op->op_targ);
15381609
if (LIKELY(
@@ -1559,17 +1630,23 @@ gettimeofday()
15591630
void
15601631
time()
15611632
PREINIT:
1562-
struct timeval Tp;
15631633
SV* rsv;
15641634
NV RETVAL;
1565-
CODE:
1635+
#ifndef HAS_NV_GETTIMEOFDAY
1636+
struct timeval Tp;
15661637
int status;
1638+
#endif
1639+
CODE:
1640+
#ifndef HAS_NV_GETTIMEOFDAY
15671641
status = gettimeofday (&Tp, NULL);
15681642
if (status == 0) {
15691643
RETVAL = Tp.tv_sec + (Tp.tv_usec / NV_1E6);
15701644
} else {
15711645
RETVAL = -1.0;
15721646
}
1647+
#else
1648+
RETVAL = nv_gettimeofday();
1649+
#endif
15731650
TMR_TARGn(rsv, RETVAL, 1);
15741651
PUSHs(rsv); /* 0 in, 1 out, entersub guarenteed 1 slot */
15751652
PUTBACK;
@@ -1739,17 +1816,27 @@ void
17391816
clock_gettime(clock_id = CLOCK_REALTIME)
17401817
clockid_t clock_id
17411818
PREINIT:
1819+
#ifndef TIME_HIRES_NV_CLOCK_GETTIME
17421820
struct timespec ts;
17431821
int status;
1822+
#endif
17441823
SV* rsv;
17451824
NV RETVAL;
17461825
PPCODE:
17471826
# ifdef TIME_HIRES_CLOCK_GETTIME_SYSCALL
17481827
status = syscall(SYS_clock_gettime, clock_id, &ts);
17491828
# else
1829+
# ifndef TIME_HIRES_NV_CLOCK_GETTIME
17501830
status = clock_gettime(clock_id, &ts);
1751-
# endif
17521831
RETVAL = status == 0 ? ts.tv_sec + (NV) ts.tv_nsec / NV_1E9 : -1;
1832+
# else
1833+
{
1834+
bool status;
1835+
NV nv = nv_clock_gettime(clock_id, &status);
1836+
RETVAL = status == 0 ? nv : -1;
1837+
}
1838+
# endif
1839+
# endif
17531840
TMR_TARGn(rsv, RETVAL, 1);
17541841
PUSHs(rsv); /* 0 or 1 in, 1 out, PPCODE: did rewind */
17551842
PUTBACK;

0 commit comments

Comments
 (0)