This is the mail archive of the
newlib@sourceware.org
mailing list for the newlib project.
improve printf locality
- From: Eric Blake <ebb9 at byu dot net>
- To: newlib at sources dot redhat dot com
- Date: Thu, 10 May 2007 07:06:23 -0600
- Subject: improve printf locality
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On cygwin, where newlib is configured with --enable-newlib-io-long-double,
print consumed more than an entire page of virtual memory in stack space!
This causes unnecessary page faults, decreased cache locality, and other
potential slowdowns. It was because at one point in history, newlib used
to stack allocate the worst case length for %Lf output (if you have a
128-bit long double with a 15 bit exponent, this can result in more than
4900 characters). But now newlib uses struct _reent to manage %f output,
so the storage claimed by buf[BUF] was 90% unused.
This also fixes some problems with the radix character (if newlib ever
supports non-C locales, it must use the radix in a thread-safe and
consistent manner), and optimizes %S processing to avoid malloc for short
strings.
OK to apply?
2007-05-10 Eric Blake <ebb9@byu.net>
* libc/stdio/vfprintf.c (_VFPRINTF_R): Fix use of decimal point
in %f and %e. Avoid malloc when possible for %S.
(BUF): Improve stack locality by using smaller size.
(MAXEXP): Define.
(exponent): Use for smaller stack size.
- --
Don't work too hard, make some time for fun as well!
Eric Blake ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFGQxjP84KuGfSFAYARAkHVAJ9h+3AtSHn/GguQUdaRR/D1x5CYwgCgghzJ
bvJXSbMXLwfyIKFu28pNrmc=
=BdL0
-----END PGP SIGNATURE-----
Index: libc/stdio/vfprintf.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio/vfprintf.c,v
retrieving revision 1.56
diff -u -p -r1.56 vfprintf.c
--- libc/stdio/vfprintf.c 8 May 2007 03:59:13 -0000 1.56
+++ libc/stdio/vfprintf.c 10 May 2007 13:03:14 -0000
@@ -229,34 +229,41 @@ _DEFUN(__sbprintf, (rptr, fp, fmt, ap),
#ifdef FLOATING_POINT
-#include <locale.h>
-#include <math.h>
-#include "floatio.h"
+# include <locale.h>
+# include <math.h>
-#if ((MAXEXP+MAXFRACT+1) > MB_LEN_MAX)
-# define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
-#else
-# define BUF MB_LEN_MAX
-#endif
-
-#define DEFPREC 6
+/* For %La, an exponent of 15 bits occupies the exponent character, a
+ sign, and up to 5 digits. */
+# define MAXEXP 7
+# define DEFPREC 6
-#ifdef _NO_LONGDBL
+# ifdef _NO_LONGDBL
static char *
_EXFUN(cvt, (struct _reent *, double, int, int, char *, int *, int, int *));
-#else
+# else
static char *
_EXFUN(cvt, (struct _reent *, _LONG_DOUBLE, int, int, char *, int *, int, int *));
extern int _EXFUN(_ldcheck,(_LONG_DOUBLE *));
-#endif
+# endif
static int _EXFUN(exponent, (char *, int, int));
-#else /* no FLOATING_POINT */
+#endif /* FLOATING_POINT */
+/* BUF must be big enough for the maximum %#llo (assuming long long is
+ at most 64 bits, this would be 23 characters), the maximum
+ multibyte character, and the maximum precision of %La (assuming
+ long double is at most 128 bits with 113 bits of mantissa, this
+ would be 31 characters). %e, %f, and %g use reentrant storage
+ shared with mprec. All other formats that use buf get by with
+ fewer characters. Making BUF slightly bigger reduces the need for
+ malloc in %a and %S, when large precision or long strings are
+ processed. */
#define BUF 40
-
-#endif /* FLOATING_POINT */
+#if defined _MB_CAPABLE && MB_LEN_MAX > BUF
+# undef BUF
+# define BUF MB_LEN_MAX
+#endif
#ifndef _NO_LONGLONG
#define quad_t long long
@@ -375,20 +382,20 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap)
int prec; /* precision from format (%.3d), or -1 */
char sign; /* sign prefix (' ', '+', '-', or \0) */
#ifdef FLOATING_POINT
- char *decimal_point = localeconv()->decimal_point;
+ char *decimal_point = _localeconv_r (data)->decimal_point;
char softsign; /* temporary negative sign for floats */
-#ifdef _NO_LONGDBL
+# ifdef _NO_LONGDBL
union { int i; double d; } _double_ = {0};
- #define _fpvalue (_double_.d)
-#else
+# define _fpvalue (_double_.d)
+# else
union { int i; _LONG_DOUBLE ld; } _long_double_ = {0};
- #define _fpvalue (_long_double_.ld)
- int tmp;
-#endif
+# define _fpvalue (_long_double_.ld)
+ int tmp;
+# endif
int expt; /* integer value of exponent */
int expsize = 0; /* character count for expstr */
int ndig = 0; /* actual number of digits returned by cvt */
- char expstr[7]; /* buffer for exponent string */
+ char expstr[MAXEXP]; /* buffer for exponent string */
#endif
u_quad_t _uquad; /* integer arguments %[diouxX] */
enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
@@ -399,7 +406,7 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap)
#define NIOV 8
struct __suio uio; /* output information: summary */
struct __siov iov[NIOV];/* ... and individual io vectors */
- char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
+ char buf[BUF]; /* space for %c, %S, %[diouxX], %[aA] */
char ox[2]; /* space for 0x hex-prefix */
#ifdef _MB_CAPABLE
wchar_t wc;
@@ -1006,31 +1013,35 @@ reswitch: switch (ch) {
}
}
else {
- if ((size = (int)_wcsrtombs_r (data,
+ if ((size = (int)_wcsrtombs_r (data,
NULL, &wcp, 0, &ps)) == -1) {
fp->_flags |= __SERR;
goto error;
}
wcp = (_CONST wchar_t *)cp;
}
-
+
if (size == 0)
break;
-
- if ((malloc_buf =
- (char *)_malloc_r (data, size + 1)) == NULL) {
- fp->_flags |= __SERR;
- goto error;
- }
-
+
+ if (size >= BUF) {
+ if ((malloc_buf =
+ (char *)_malloc_r (data, size + 1))
+ == NULL) {
+ fp->_flags |= __SERR;
+ goto error;
+ }
+ cp = malloc_buf;
+ } else
+ cp = buf;
+
/* Convert widechar string to multibyte string. */
memset ((_PTR)&ps, '\0', sizeof (mbstate_t));
- if (_wcsrtombs_r (data, malloc_buf,
- &wcp, size, &ps) != size) {
+ if (_wcsrtombs_r (data, cp,
+ &wcp, size, &ps) != size) {
fp->_flags |= __SERR;
goto error;
}
- cp = malloc_buf;
cp[size] = '\0';
}
#endif /* _MB_CAPABLE */
@@ -1216,18 +1227,18 @@ number: if ((dprec = prec) >= 0)
PRINT (cp, ndig);
PAD (expt - ndig, zeroes);
if (flags & ALT)
- PRINT (".", 1);
+ PRINT (decimal_point, 1);
} else {
PRINT (cp, expt);
cp += expt;
- PRINT (".", 1);
+ PRINT (decimal_point, 1);
PRINT (cp, ndig - expt);
}
} else { /* 'e' or 'E' */
if (ndig > 1 || flags & ALT) {
- ox[0] = *cp++;
- ox[1] = '.';
- PRINT (ox, 2);
+ PRINT (cp, 1);
+ cp++;
+ PRINT (decimal_point, 1);
if (_fpvalue) {
PRINT (cp, ndig - 1);
} else /* 0.[0..] */
@@ -1370,7 +1381,7 @@ _DEFUN(exponent, (p0, exp, fmtch),
int fmtch)
{
register char *p, *t;
- char expbuf[40];
+ char expbuf[MAXEXP];
p = p0;
*p++ = fmtch;
@@ -1380,13 +1391,13 @@ _DEFUN(exponent, (p0, exp, fmtch),
}
else
*p++ = '+';
- t = expbuf + 40;
+ t = expbuf + MAXEXP;
if (exp > 9) {
do {
*--t = to_char (exp % 10);
} while ((exp /= 10) > 9);
*--t = to_char (exp);
- for (; t < expbuf + 40; *p++ = *t++);
+ for (; t < expbuf + MAXEXP; *p++ = *t++);
}
else {
*p++ = '0';