develooper Front page | perl.perl5.porters | Postings from November 2003

[PATCH 5.8.2 @21574] sprintf() painfully slow

Thread Next
From:
Ilya Zakharevich
Date:
November 3, 2003 20:27
Subject:
[PATCH 5.8.2 @21574] sprintf() painfully slow
Message ID:
20031104042739.GA1697@math.berkeley.edu
I do not know whether the fact that an operation is an order of magnitude
slower than it must be should be considered a bug...

This patch speeds up rounding-to-integer (which is "%.0f") about 15x,
and g-format sprintf() about 1.5x.  Note that g-formats of the form
%.<digits>g are now routed through Gconvert().  Do not know whether
there are versions fo Gconvert() which are good enough for NV-to-PV
conversion, but not good enough for sprintf() emulation.  If there
are, this routing should be made optional.

Enjoy,
Ilya

P.S.  It should be easy to special-case %.<digits>f too (when
      <digits>+3 <= sizeof(ebuf)).  Change F0_convert() to
      F0_convert_u() (which takes positives only), then massage the output.

--- ./sv.c-pre	Wed Oct 22 13:33:14 2003
+++ ./sv.c	Mon Nov  3 20:00:42 2003
@@ -8240,6 +8240,33 @@ S_expect_number(pTHX_ char** pattern)
 }
 #define EXPECT_NUMBER(pattern, var) (var = S_expect_number(aTHX_ &pattern))
 
+static char *
+F0convert(NV nv, char *endbuf, STRLEN *len)
+{
+    int neg = nv < 0;
+    UV uv;
+    char *p = endbuf;
+
+    if (neg)
+	nv = -nv;
+    if (nv < UV_MAX) {
+	nv += 0.5;
+	uv = nv;
+	if (uv & 1 && uv == nv)
+	    uv--;			/* Round to even */
+	do {
+	    unsigned dig = uv % 10;
+	    *--p = '0' + dig;
+	} while (uv /= 10);
+	if (neg)
+	    *--p = '-';
+	*len = endbuf - p;
+	return p;
+    }
+    return Nullch;
+}
+
+
 /*
 =for apidoc sv_vcatpvfn
 
@@ -8267,6 +8294,12 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const cha
     bool has_utf8; /* has the result utf8? */
     bool pat_utf8; /* the pattern is in utf8? */
     SV *nsv = Nullsv;
+    /* Times 4: a decimal digit takes more than 3 binary digits.
+     * NV_DIG: mantissa takes than many decimal digits.
+     * Plus 32: Playing safe. */
+    char ebuf[IV_DIG * 4 + NV_DIG + 32];
+    /* large enough for "%#.#f" --chip */
+    /* what about long double NVs? --jhi */
 
     has_utf8 = pat_utf8 = DO_UTF8(sv);
 
@@ -8302,6 +8335,40 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const cha
 	}
     }
 
+    /* special-case "%.<number>[gf]" */
+    if ( patlen <= 5 && pat[0] == '%' && pat[1] == '.'
+	 && (pat[patlen-1] == 'g' || pat[patlen-1] == 'f') ) {
+	unsigned digits = 0;
+	const char *pp;
+
+	pp = pat + 2;
+	while (*pp >= '0' && *pp <= '9')
+	    digits = 10 * digits + (*pp++ - '0');
+	if (pp - pat == patlen - 1) {
+	    NV nv;
+
+	    if (args)
+		nv = (NV)va_arg(*args, double);
+	    else if (svix < svmax)
+		nv = SvNV(*svargs);
+	    else
+		return;
+	    if (*pp == 'g') {
+		Gconvert((double)nv, (digits ? digits : 1), 0, ebuf);
+		sv_catpv(sv, ebuf);
+		if (*ebuf)	/* May return an empty string for digits==0 */
+		    return;
+	    } else if (!digits) {
+		STRLEN l;
+
+		if ((p = F0convert(nv, ebuf + sizeof ebuf, &l))) {
+		    sv_catpvn(sv, p, l);
+		    return;
+		}
+	    }
+	}
+    }
+
     if (!args && svix < svmax && DO_UTF8(*svargs))
 	has_utf8 = TRUE;
 
@@ -8333,13 +8400,6 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const cha
 
 	char *eptr = Nullch;
 	STRLEN elen = 0;
-	/* Times 4: a decimal digit takes more than 3 binary digits.
-	 * NV_DIG: mantissa takes than many decimal digits.
-	 * Plus 32: Playing safe. */
-	char ebuf[IV_DIG * 4 + NV_DIG + 32];
-	/* large enough for "%#.#f" --chip */
-	/* what about long double NVs? --jhi */
-
 	SV *vecsv = Nullsv;
 	U8 *vecstr = Null(U8*);
 	STRLEN veclen = 0;
@@ -8999,6 +9059,17 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const cha
 		PL_efloatbuf[0] = '\0';
 	    }
 
+	    if ( !(width || left || plus || alt) && fill != '0'
+		 && has_precis && intsize != 'q' ) {	/* Shortcuts */
+		if ( c == 'g') {
+		    Gconvert((double)nv, precis, 0, PL_efloatbuf);
+		    if (*PL_efloatbuf)	/* May return an empty string for digits==0 */
+			goto float_converted;
+		} else if ( c == 'f' && !precis) {
+		    if ((eptr = F0convert(nv, ebuf + sizeof ebuf, &elen)))
+			break;
+		}
+	    }
 	    eptr = ebuf + sizeof ebuf;
 	    *--eptr = '\0';
 	    *--eptr = c;
@@ -9043,6 +9114,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const cha
 #else
 	    (void)sprintf(PL_efloatbuf, eptr, nv);
 #endif
+	float_converted:
 	    eptr = PL_efloatbuf;
 	    elen = strlen(PL_efloatbuf);
 	    break;

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About