Skip to content

Commit 0ed64dc

Browse files
committed
Time::HiRes aggressive max perf implementation of dXSTARG; TARGi_u_n(,);
-reason, make these XSUBs as fast as possible so these XSUBs are more accurate for benchmarking, or contribute less overhead to the final numeric time deltas vs the time of whatever PP code was being measured The sv_newmortal()+sv_set_i_u_n_v_mg() permutation is unacceptable. Stepping into sv_upgrade() is unacceptable to do SVt_NULL->SVt_IV. -TMR_TARG***(rsv, RETVAL, 1); macros could be further optimized here vs pp.h's impl of TARG***(RETVAL,1), but that is left for the future.
1 parent 78ec3d4 commit 0ed64dc

File tree

1 file changed

+190
-31
lines changed

1 file changed

+190
-31
lines changed

dist/Time-HiRes/HiRes.xs

Lines changed: 190 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,117 @@
7474
# define THR_newSVsv_cow(sv) newSVsv_flags((sv), SV_GMAGIC|SV_NOSTEAL)
7575
#endif
7676

77+
/* PL_op->op_private & OPpENTERSUB_HASTARG feature was added in
78+
79+
d30110745a - Ilya Zakharevich -8/26/1999 11:33:01 PM - 5.5.61
80+
Speeding up XSUB calls up to 66%
81+
Addendum: it's "only" 33% speedup.
82+
83+
These 3 are highly optimized version of 3 macros from pp.h that were
84+
purpose made mostly for EU::PXS's private use, but we DO NOT want to execute
85+
a slower sv_newmortal() + sv_set_i_u_n_v_mg(), instead of
86+
sv_2mortal(newSV_i_u_n_v()).
87+
88+
These macros do not put the new SV* on the stack. Caller is responsible for
89+
that.
90+
91+
Arg _nsv is an uninitialized SV* variable, a new SV* will be placed in
92+
the _nsv var. SvREFCNT()/SV* lifecycle details are handled by the macro.
93+
The caller IS NOT allowed to execute a "sv_2mortal(_nsv);" on the new SV*.
94+
95+
sv_set_i_u_n_v_mg() is required to a huge amount of safety checks like
96+
de-COW PVs RVs, COWs, sv_upgrade(), copy old SV body contents to a higher
97+
order SV body, etc.
98+
99+
Also if G_LIST context, we do not want Perl_leave_adjust_stacks() to create
100+
a mortal copy of our PAD SV* TARG. Example of returning a dXSTARG, and
101+
Perl_leave_adjust_stacks() instantly makes a mortal dup of it is this code
102+
$self->logtime(time());
103+
*/
104+
105+
106+
107+
/* set TARG to the IV value i. If do_taint is false,
108+
* assume that PL_tainted can never be true */
109+
#define TMR_TARGi(_nsv, i, do_taint) \
110+
STMT_START { \
111+
IV TARGi_iv = i; \
112+
if (GIMME_V == G_LIST || !(PL_op->op_private & OPpENTERSUB_HASTARG)) \
113+
_nsv = sv_2mortal(newSViv(TARGi_iv)); \
114+
else { \
115+
_nsv = PAD_SV(PL_op->op_targ); \
116+
if (LIKELY( \
117+
((SvFLAGS(_nsv) & (SVTYPEMASK|SVf_THINKFIRST|SVf_IVisUV)) == SVt_IV) \
118+
& (do_taint ? !TAINT_get : 1))) \
119+
{ \
120+
/* Cheap SvIOK_only(). \
121+
* Assert that flags which SvIOK_only() would test or \
122+
* clear can't be set, because we're SVt_IV */ \
123+
assert(!(SvFLAGS(_nsv) & \
124+
(SVf_OOK|SVf_UTF8|(SVf_OK & ~(SVf_IOK|SVp_IOK))))); \
125+
SvFLAGS(_nsv) |= (SVf_IOK|SVp_IOK); \
126+
/* SvIV_set() where sv_any points to head */ \
127+
_nsv->sv_u.svu_iv = TARGi_iv; \
128+
} \
129+
else \
130+
sv_setiv_mg(_nsv, TARGi_iv); \
131+
} \
132+
} STMT_END
133+
134+
/* set TARG to the UV value u. If do_taint is false,
135+
* assume that PL_tainted can never be true */
136+
#define TMR_TARGu(_nsv, u, do_taint) \
137+
STMT_START { \
138+
UV TARGu_uv = u; \
139+
if (GIMME_V == G_LIST || !(PL_op->op_private & OPpENTERSUB_HASTARG)) \
140+
_nsv = sv_2mortal(newSVuv(TARGu_uv)); \
141+
else { \
142+
_nsv = PAD_SV(PL_op->op_targ); \
143+
if (LIKELY( \
144+
((SvFLAGS(_nsv) & (SVTYPEMASK|SVf_THINKFIRST|SVf_IVisUV)) == SVt_IV) \
145+
& (do_taint ? !TAINT_get : 1) \
146+
& (TARGu_uv <= (UV)IV_MAX))) \
147+
{ \
148+
/* Cheap SvIOK_only(). \
149+
* Assert that flags which SvIOK_only() would test or \
150+
* clear can't be set, because we're SVt_IV */ \
151+
assert(!(SvFLAGS(_nsv) & \
152+
(SVf_OOK|SVf_UTF8|(SVf_OK & ~(SVf_IOK|SVp_IOK))))); \
153+
SvFLAGS(_nsv) |= (SVf_IOK|SVp_IOK); \
154+
/* SvIV_set() where sv_any points to head */ \
155+
_nsv->sv_u.svu_iv = TARGu_uv; \
156+
} \
157+
else \
158+
sv_setuv_mg(_nsv, TARGu_uv); \
159+
} \
160+
} STMT_END
161+
162+
/* set TARG to the NV value n. If do_taint is false,
163+
* assume that PL_tainted can never be true */
164+
#define TMR_TARGn(_nsv, n, do_taint) \
165+
STMT_START { \
166+
NV TARGn_nv = n; \
167+
if (GIMME_V == G_LIST || !(PL_op->op_private & OPpENTERSUB_HASTARG)) \
168+
_nsv = sv_2mortal(newSVnv(TARGn_nv)); \
169+
else { \
170+
_nsv = PAD_SV(PL_op->op_targ); \
171+
if (LIKELY( \
172+
((SvFLAGS(_nsv) & (SVTYPEMASK|SVf_THINKFIRST)) == SVt_NV) \
173+
& (do_taint ? !TAINT_get : 1))) \
174+
{ \
175+
/* Cheap SvNOK_only(). \
176+
* Assert that flags which SvNOK_only() would test or \
177+
* clear can't be set, because we're SVt_NV */ \
178+
assert(!(SvFLAGS(_nsv) & \
179+
(SVf_OOK|SVf_UTF8|(SVf_OK & ~(SVf_NOK|SVp_NOK))))); \
180+
SvFLAGS(_nsv) |= (SVf_NOK|SVp_NOK); \
181+
SvNV_set(_nsv, TARGn_nv); \
182+
} \
183+
else \
184+
sv_setnv_mg(_nsv, TARGn_nv); \
185+
} \
186+
} STMT_END
187+
77188
#define IV_1E6 1000000
78189
#define IV_1E7 10000000
79190
#define IV_1E9 1000000000
@@ -1143,11 +1254,13 @@ INCLUDE: const-xs.inc
11431254

11441255
#if defined(HAS_USLEEP) && defined(HAS_GETTIMEOFDAY)
11451256

1146-
NV
1257+
void
11471258
usleep(useconds)
11481259
NV useconds
11491260
PREINIT:
11501261
struct timeval Ta, Tb;
1262+
SV* rsv;
1263+
NV RETVAL;
11511264
CODE:
11521265
gettimeofday(&Ta, NULL);
11531266
if (items > 0) {
@@ -1173,17 +1286,19 @@ usleep(useconds)
11731286
printf("[%ld %ld] [%ld %ld]\n", Tb.tv_sec, Tb.tv_usec, Ta.tv_sec, Ta.tv_usec);
11741287
# endif
11751288
RETVAL = NV_1E6*(Tb.tv_sec-Ta.tv_sec)+(NV)((IV)Tb.tv_usec-(IV)Ta.tv_usec);
1176-
1177-
OUTPUT:
1178-
RETVAL
1289+
TMR_TARGn(rsv, RETVAL, 1);
1290+
SETs(rsv);
1291+
return; /* no PUTBACK no PUSH, 1 in, 1 out */
11791292

11801293
# if defined(TIME_HIRES_NANOSLEEP)
11811294

1182-
NV
1295+
void
11831296
nanosleep(nsec)
11841297
NV nsec
11851298
PREINIT:
11861299
struct timespec sleepfor, unslept;
1300+
SV* rsv;
1301+
NV RETVAL;
11871302
CODE:
11881303
if (nsec < 0.0)
11891304
croak("%s(%" NVgf "%s", "Time::HiRes::nanosleep", nsec,
@@ -1194,8 +1309,9 @@ nanosleep(nsec)
11941309
} else {
11951310
RETVAL = nsec_without_unslept(&sleepfor, &unslept);
11961311
}
1197-
OUTPUT:
1198-
RETVAL
1312+
TMR_TARGn(rsv, RETVAL, 1);
1313+
SETs(rsv);
1314+
return; /* no PUTBACK no PUSH, 1 in, 1 out */
11991315

12001316
# else /* #if defined(TIME_HIRES_NANOSLEEP) */
12011317

@@ -1211,11 +1327,13 @@ nanosleep(nsec)
12111327

12121328
# endif /* #if defined(TIME_HIRES_NANOSLEEP) */
12131329

1214-
NV
1330+
void
12151331
sleep(...)
12161332
PREINIT:
12171333
struct timeval Ta, Tb;
1218-
CODE:
1334+
SV* rsv;
1335+
NV RETVAL;
1336+
PPCODE:
12191337
gettimeofday(&Ta, NULL);
12201338
if (items > 0) {
12211339
NV seconds = SvNV(ST(0));
@@ -1250,9 +1368,10 @@ sleep(...)
12501368
printf("[%ld %ld] [%ld %ld]\n", Tb.tv_sec, Tb.tv_usec, Ta.tv_sec, Ta.tv_usec);
12511369
# endif
12521370
RETVAL = (NV)(Tb.tv_sec-Ta.tv_sec)+0.000001*(NV)(Tb.tv_usec-Ta.tv_usec);
1253-
1254-
OUTPUT:
1255-
RETVAL
1371+
TMR_TARGn(rsv, RETVAL, 1);
1372+
PUSHs(rsv);
1373+
PUTBACK;
1374+
return;
12561375

12571376
#else /* #if defined(HAS_USLEEP) && defined(HAS_GETTIMEOFDAY) */
12581377

@@ -1270,11 +1389,14 @@ usleep(useconds)
12701389

12711390
#ifdef HAS_UALARM
12721391

1273-
IV
1392+
void
12741393
ualarm(useconds,uinterval=0)
12751394
int useconds
12761395
int uinterval
1277-
CODE:
1396+
PREINIT:
1397+
SV* rsv;
1398+
IV RETVAL;
1399+
PPCODE:
12781400
if (useconds < 0 || uinterval < 0)
12791401
croak("%s(%d, %d%s",
12801402
"Time::HiRes::ualarm", useconds, uinterval,
@@ -1298,15 +1420,19 @@ ualarm(useconds,uinterval=0)
12981420

12991421
RETVAL = ualarm(useconds, uinterval);
13001422
# endif
1423+
TMR_TARGi(rsv, RETVAL, 1);
1424+
PUSHs(rsv);
1425+
PUTBACK;
1426+
return;
13011427

1302-
OUTPUT:
1303-
RETVAL
1304-
1305-
NV
1428+
void
13061429
alarm(seconds,interval=0)
13071430
NV seconds
13081431
NV interval
1309-
CODE:
1432+
PREINIT:
1433+
SV* rsv;
1434+
NV RETVAL;
1435+
PPCODE:
13101436
if (seconds < 0.0 || interval < 0.0)
13111437
croak("%s(%" NVgf ", %" NVgf "%s",
13121438
"Time::HiRes::alarm", seconds, interval,
@@ -1348,9 +1474,10 @@ alarm(seconds,interval=0)
13481474
RETVAL = (NV)ualarm( useconds, uinterval ) / NV_1E6;
13491475
# endif
13501476
}
1351-
1352-
OUTPUT:
1353-
RETVAL
1477+
TMR_TARGn(rsv, RETVAL, 1);
1478+
PUSHs(rsv);
1479+
PUTBACK;
1480+
return;
13541481

13551482
#else /* #ifdef HAS_UALARM */
13561483

@@ -1387,6 +1514,7 @@ gettimeofday()
13871514
PREINIT:
13881515
struct timeval Tp;
13891516
int status;
1517+
OP* const op = PL_op;
13901518
U8 is_G_LIST = GIMME_V == G_LIST;
13911519
PPCODE:
13921520
if (is_G_LIST)
@@ -1399,15 +1527,41 @@ gettimeofday()
13991527
PUSHs(sv_2mortal(newSViv(sec)));
14001528
PUSHs(sv_2mortal(newSViv(usec)));
14011529
} else {
1530+
/* no Perl_leave_adjust_stacks() hazard here,
1531+
only a PP vs call_sv() hazard */
14021532
NV nv = Tp.tv_sec + (Tp.tv_usec / NV_1E6);
1403-
PUSHs(sv_2mortal(newSVnv(nv)));
1533+
const U8 do_taint = 1;
1534+
NV TARGn_nv = nv;
1535+
SV* rsv;
1536+
if (op->op_private & OPpENTERSUB_HASTARG) {
1537+
rsv = PAD_SV(op->op_targ);
1538+
if (LIKELY(
1539+
((SvFLAGS(rsv) & (SVTYPEMASK|SVf_THINKFIRST)) == SVt_NV)
1540+
& (do_taint ? !TAINT_get : 1)))
1541+
{
1542+
/* Cheap SvNOK_only().
1543+
* Assert that flags which SvNOK_only() would test or
1544+
* clear can't be set, because we're SVt_NV */
1545+
assert(!(SvFLAGS(rsv) &
1546+
(SVf_OOK|SVf_UTF8|(SVf_OK & ~(SVf_NOK|SVp_NOK)))));
1547+
SvFLAGS(rsv) |= (SVf_NOK|SVp_NOK);
1548+
SvNV_set(rsv, TARGn_nv);
1549+
}
1550+
else
1551+
sv_setnv_mg(rsv, TARGn_nv);
1552+
}
1553+
else
1554+
rsv = sv_2mortal(newSVnv(TARGn_nv));
1555+
PUSHs(rsv);
14041556
}
14051557
}
14061558

1407-
NV
1559+
void
14081560
time()
14091561
PREINIT:
14101562
struct timeval Tp;
1563+
SV* rsv;
1564+
NV RETVAL;
14111565
CODE:
14121566
int status;
14131567
status = gettimeofday (&Tp, NULL);
@@ -1416,8 +1570,10 @@ time()
14161570
} else {
14171571
RETVAL = -1.0;
14181572
}
1419-
OUTPUT:
1420-
RETVAL
1573+
TMR_TARGn(rsv, RETVAL, 1);
1574+
PUSHs(rsv); /* 0 in, 1 out, entersub guarenteed 1 slot */
1575+
PUTBACK;
1576+
return;
14211577

14221578
#endif /* #ifdef HAS_GETTIMEOFDAY */
14231579

@@ -1579,22 +1735,25 @@ utime(accessed, modified, ...)
15791735

15801736
#if defined(TIME_HIRES_CLOCK_GETTIME)
15811737

1582-
NV
1738+
void
15831739
clock_gettime(clock_id = CLOCK_REALTIME)
15841740
clockid_t clock_id
15851741
PREINIT:
15861742
struct timespec ts;
15871743
int status;
1588-
CODE:
1744+
SV* rsv;
1745+
NV RETVAL;
1746+
PPCODE:
15891747
# ifdef TIME_HIRES_CLOCK_GETTIME_SYSCALL
15901748
status = syscall(SYS_clock_gettime, clock_id, &ts);
15911749
# else
15921750
status = clock_gettime(clock_id, &ts);
15931751
# endif
15941752
RETVAL = status == 0 ? ts.tv_sec + (NV) ts.tv_nsec / NV_1E9 : -1;
1595-
1596-
OUTPUT:
1597-
RETVAL
1753+
TMR_TARGn(rsv, RETVAL, 1);
1754+
PUSHs(rsv); /* 0 or 1 in, 1 out, PPCODE: did rewind */
1755+
PUTBACK;
1756+
return;
15981757

15991758
#else /* if defined(TIME_HIRES_CLOCK_GETTIME) */
16001759

0 commit comments

Comments
 (0)