Hi,When SvNV() macro is called with an SV_IV argument, it upgrades the SV to an SV_PVNV and caches there the IV as an NV, but this aproach can actually be counterproductive because it increases memory usage and the processor cache performance decreases. On the other hand, and specially, after the last changes done by Nicholas, accessing SV_IV values doesn't (almost) ever incur on a cache miss and converting from an integer to a float is a cheap operation on most modern CPUs.
One of the places where converting IVs to PVNVs actually degrades performance is when numerically sorting a list of IVs, for instance:
@foo = sort { $a <=> $b } (1, 2) x 10000;
Some benchmarks performed on my computer report:
nv => uses SvNV() macro to get NVs
nsiv => uses a new SvNSIV() macro to get NVs. NSIV stands
for Number or Signed Integer Value
COMPARING MEMORY USAGE
sorting 100000 NVs:
test: nsiv, memory used: 0 bytes
test: nv, memory used: 0 bytes
sorting 100000 IVs:
test: nsiv, memory used: 0 bytes
test: nv, memory used: 4055040 bytes
sorting 100000 mixed NVs and IVs:
test: nsiv, memory used: 0 bytes
test: nv, memory used: 2027520 bytes
COMPARING PERFORMANCE
sorting 100000 NVs:
Rate nv nsiv
nv 1.32/s -- -1%
nsiv 1.32/s 1% --
sorting 100000 IVs:
Rate nv nsiv
nv 1.47/s -- -24%
nsiv 1.92/s 31% --
sorting 100000 mixed NVs and IVs:
Rate nv nsiv
nv 1.56/s -- -8%
nsiv 1.69/s 8% --
I am including here two patches:
One with the instrumentalization required to perform the
benchmarks, and another that just commits to the NSIV version.
The benchmark script is also included. Cheers, - Salvador.
bm_nsiv_sort-0.01.pl
Description: Perl program
--- perl-current/pp_sort.c 2005-06-06 11:41:13.741648408 +0100
+++ my-perl-current/pp_sort.c 2005-06-06 11:48:34.699612608 +0100
@@ -1488,6 +1488,10 @@
sortsvp(aTHX_ array, nmemb, cmp, 1);
}
+#define SvNSIOK(sv) ((SvFLAGS(sv) & SVf_NOK) || ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK))
+#define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK)
+#define SvNSIV(sv) ( SvNOK(sv) ? SvNVX(sv) : ( SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv) ) )
+
PP(pp_sort)
{
dVAR; dSP; dMARK; dORIGMARK;
@@ -1507,6 +1511,7 @@
U8 flags = PL_op->op_flags;
void (*sortsvp)(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp)
= Perl_sortsv;
+ I32 all_SIVs = 1;
if (gimme != G_ARRAY) {
SP = MARK;
@@ -1610,12 +1615,14 @@
}
}
else {
- if (!SvNOK(*p1)) {
+ if (!SvNSIOK(*p1)) {
if (SvAMAGIC(*p1))
overloading = 1;
else
(void)sv_2nv(*p1);
}
+ if (all_SIVs && !SvSIOK(*p1))
+ all_SIVs = 0;
}
}
else {
@@ -1692,9 +1699,9 @@
start = sorting_av ? AvARRAY(av) : ORIGMARK+1;
sortsvp(aTHX_ start, max,
(priv & OPpSORT_NUMERIC)
- ? ( (priv & OPpSORT_INTEGER)
+ ? ( ( ( priv & OPpSORT_INTEGER) || all_SIVs)
? ( overloading ? amagic_i_ncmp : sv_i_ncmp)
- : ( overloading ? amagic_ncmp : sv_ncmp))
+ : ( overloading ? amagic_ncmp : sv_ncmp ) )
: ( IN_LOCALE_RUNTIME
? ( overloading
? amagic_cmp_locale
@@ -1832,8 +1839,8 @@
static I32
sv_ncmp(pTHX_ SV *a, SV *b)
{
- NV nv1 = SvNV(a);
- NV nv2 = SvNV(b);
+ NV nv1 = SvNSIV(a);
+ NV nv2 = SvNSIV(b);
return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}
--- perl-current/pp_sort.c 2005-06-06 11:41:13.741648408 +0100
+++ my-perl-current/pp_sort.c 2005-06-06 11:39:30.311372200 +0100
@@ -38,6 +38,7 @@
static I32 sortcv_xsub(pTHX_ SV *a, SV *b);
static I32 sv_ncmp(pTHX_ SV *a, SV *b);
static I32 sv_i_ncmp(pTHX_ SV *a, SV *b);
+static I32 sv_nsicmp(pTHX_ SV *a, SV *b);
static I32 amagic_ncmp(pTHX_ SV *a, SV *b);
static I32 amagic_i_ncmp(pTHX_ SV *a, SV *b);
static I32 amagic_cmp(pTHX_ SV *a, SV *b);
@@ -1488,6 +1489,10 @@
sortsvp(aTHX_ array, nmemb, cmp, 1);
}
+#define SvNSIOK(sv) ((SvFLAGS(sv) & SVf_NOK) || ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK))
+#define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK)
+#define SvNSIV(sv) ( SvNOK(sv) ? SvNVX(sv) : ( SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv) ) )
+
PP(pp_sort)
{
dVAR; dSP; dMARK; dORIGMARK;
@@ -1507,6 +1512,8 @@
U8 flags = PL_op->op_flags;
void (*sortsvp)(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp)
= Perl_sortsv;
+ int use_nsiv_macro = get_sv("sort::use_nsiv_macro", 0) ? SvIV(get_sv("sort::use_nsiv_macro", 0)) : 0;
+ /* int all_SIVs = use_nsiv_macro; */
if (gimme != G_ARRAY) {
SP = MARK;
@@ -1610,11 +1617,24 @@
}
}
else {
- if (!SvNOK(*p1)) {
- if (SvAMAGIC(*p1))
- overloading = 1;
- else
- (void)sv_2nv(*p1);
+ if (use_nsiv_macro) {
+ if (!SvNSIOK(*p1)) {
+ if (SvAMAGIC(*p1))
+ overloading = 1;
+ else
+ (void)sv_2nv(*p1);
+ }
+ /* if (all_SIVs && !SvSIOK(*p1)) {
+ all_SIVs = 0;
+ } */
+ }
+ else {
+ if (!SvNOK(*p1)) {
+ if (SvAMAGIC(*p1))
+ overloading = 1;
+ else
+ (void)sv_2nv(*p1);
+ }
}
}
}
@@ -1692,9 +1712,9 @@
start = sorting_av ? AvARRAY(av) : ORIGMARK+1;
sortsvp(aTHX_ start, max,
(priv & OPpSORT_NUMERIC)
- ? ( (priv & OPpSORT_INTEGER)
+ ? ( ( ( priv & OPpSORT_INTEGER) /* || all_SIVs */)
? ( overloading ? amagic_i_ncmp : sv_i_ncmp)
- : ( overloading ? amagic_ncmp : sv_ncmp))
+ : ( overloading ? amagic_ncmp : ( use_nsiv_macro ? sv_nsicmp : sv_ncmp) ) )
: ( IN_LOCALE_RUNTIME
? ( overloading
? amagic_cmp_locale
@@ -1838,6 +1858,14 @@
}
static I32
+sv_nsicmp(pTHX_ SV *a, SV *b)
+{
+ NV nv1 = SvNSIV(a);
+ NV nv2 = SvNSIV(b);
+ return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
+}
+
+static I32
sv_i_ncmp(pTHX_ SV *a, SV *b)
{
IV iv1 = SvIV(a);
