This is the mail archive of the libc-hacker@sourceware.org mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] __find_specmb tweaks


Hi!

What __find_specmb does doesn't make much sense to me with any ASCII
compatible charset.  At least from looking at localedata/charmaps/*,
with the exception of ISO_10646 (ASCII incompatible) no charset has
'%' byte as part of a valid multibyte character other than '%' character
itself.  So strchrnul (format, '%'); IMHO gives exactly the same answers
- even invalid partial multibyte chars before '%' will result in mbrlen
returning -1 and thus __find_specmb stepping over just one byte.

Here are some numbers I measured.
tt1.c is:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  char buf[1000000];
  for (i = 0; i < 100; i++)
    sprintf (buf, "pppp....100000timesppppp%s\n", "p");
  return 0;
}
tt2.c is:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  char buf[1000000];
  for (i = 0; i < 100; i++)
    sprintf (buf, "\xc3\x81\xc3\x81\xc3\x81\xc3...100000times\xc3\x81\xc3\x81%s\n", "\xc3\x81");
  return 0;
}
(so tt1.c is slightly over 100000 bytes, tt2.c over 800000 bytes).

tt3.c is:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  char buf[100];
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  for (i = 0; i < 1000000; i++)
    sprintf (buf, "\xc3\x81%s\xc3\x81%s\xc3\x81%s\xc3\x81%s\xc3\x81%s", "", "", "", "", "");
  return 0;
}

tt4.c is:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  char buf[100];
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  for (i = 0; i < 1000000; i++)
    sprintf (buf, "pq%spq%spq%spq%spq%s", "", "", "", "", "");
  return 0;
}

tt5.c:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  char buf[100];
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  for (i = 0; i < 1000000; i++)
    sprintf (buf, "%s%s%s%s%s", "", "", "", "", "");
  return 0;
}

and tt6.c is:
#include <stdio.h>
#include <locale.h>

int
main (void)
{
  char buf[100];
  setlocale (LC_ALL, "en_US.UTF-8");
  int i;
  for (i = 0; i < 1000000; i++)
    sprintf (buf, " %s %s %s %s %s", "", "", "", "", "");
  return 0;
}

The numbers show significant to small improvement on tt1.c through tt4.c,
small slow down on tt5.c and unfortunately bigger slowdown on tt6.c.
This was all on i686.  Guess strchrnul there has some slightly bigger
startup costs, perhaps we could inline 2 or 3 loop iterations in __find_specmb
and only afterwards call strchrnul - no text in between format specifiers is
quite common I guess.

~/timing elf/ld.so --library-path oldish /tmp/tt1; ~/timing elf/ld.so --library-path newish /tmp/tt1
Strip out best and worst realtime result
minimum: 0.013210058 sec real / 0.000018514 sec CPU
maximum: 0.022143248 sec real / 0.000037514 sec CPU
average: 0.020361838 sec real / 0.000022762 sec CPU
stdev  : 0.002170127 sec real / 0.000004490 sec CPU
Strip out best and worst realtime result
minimum: 0.011785802 sec real / 0.000020636 sec CPU
maximum: 0.013008927 sec real / 0.000036769 sec CPU
average: 0.012917288 sec real / 0.000024649 sec CPU
stdev  : 0.000056095 sec real / 0.000001584 sec CPU

~/timing elf/ld.so --library-path oldish /tmp/tt2; ~/timing elf/ld.so --library-path newish /tmp/tt2
Strip out best and worst realtime result
minimum: 0.571823044 sec real / 0.000019525 sec CPU
maximum: 0.634448474 sec real / 0.000034545 sec CPU
average: 0.581287752 sec real / 0.000021716 sec CPU
stdev  : 0.002792534 sec real / 0.000001283 sec CPU
Strip out best and worst realtime result
minimum: 0.015345324 sec real / 0.000015912 sec CPU
maximum: 0.025850392 sec real / 0.000034497 sec CPU
average: 0.023723572 sec real / 0.000022589 sec CPU
stdev  : 0.003069815 sec real / 0.000003756 sec CPU

~/timing elf/ld.so --library-path oldish /tmp/tt3; ~/timing elf/ld.so --library-path newish /tmp/tt3
Strip out best and worst realtime result
minimum: 0.593800905 sec real / 0.000019218 sec CPU
maximum: 0.640624009 sec real / 0.000038142 sec CPU
average: 0.609940793 sec real / 0.000021613 sec CPU
stdev  : 0.009931741 sec real / 0.000002540 sec CPU
Strip out best and worst realtime result
minimum: 0.315929631 sec real / 0.000018011 sec CPU
maximum: 0.341750143 sec real / 0.000029126 sec CPU
average: 0.330474908 sec real / 0.000020035 sec CPU
stdev  : 0.004397893 sec real / 0.000001048 sec CPU

 ~/timing elf/ld.so --library-path oldish /tmp/tt4; ~/timing elf/ld.so --library-path newish /tmp/tt4
Strip out best and worst realtime result
minimum: 0.321513749 sec real / 0.000018472 sec CPU
maximum: 0.337194373 sec real / 0.000034284 sec CPU
average: 0.331968021 sec real / 0.000021245 sec CPU
stdev  : 0.003868348 sec real / 0.000001455 sec CPU
Strip out best and worst realtime result
minimum: 0.318249926 sec real / 0.000019332 sec CPU
maximum: 0.338803238 sec real / 0.000034228 sec CPU
average: 0.330834365 sec real / 0.000021034 sec CPU
stdev  : 0.004820143 sec real / 0.000000933 sec CPU

~/timing elf/ld.so --library-path oldish /tmp/tt5; ~/timing elf/ld.so --library-path newish /tmp/tt5
Strip out best and worst realtime result
minimum: 0.249704885 sec real / 0.000018716 sec CPU
maximum: 0.283555382 sec real / 0.000037899 sec CPU
average: 0.259019462 sec real / 0.000020721 sec CPU
stdev  : 0.002648020 sec real / 0.000001667 sec CPU
Strip out best and worst realtime result
minimum: 0.246840840 sec real / 0.000017392 sec CPU
maximum: 0.280116149 sec real / 0.000031962 sec CPU
average: 0.265815954 sec real / 0.000020119 sec CPU
stdev  : 0.007308481 sec real / 0.000001534 sec CPU

~/timing elf/ld.so --library-path oldish /tmp/tt6; ~/timing elf/ld.so --library-path newish /tmp/tt6
Strip out best and worst realtime result
minimum: 0.310119020 sec real / 0.000018821 sec CPU
maximum: 0.328380499 sec real / 0.000029529 sec CPU
average: 0.318935657 sec real / 0.000020565 sec CPU
stdev  : 0.004081125 sec real / 0.000001119 sec CPU
Strip out best and worst realtime result
minimum: 0.350442630 sec real / 0.000015998 sec CPU
maximum: 0.372019800 sec real / 0.000032094 sec CPU
average: 0.358078675 sec real / 0.000021110 sec CPU
stdev  : 0.004828273 sec real / 0.000002259 sec CPU


2007-10-10  Jakub Jelinek  <jakub@redhat.com>

	* stdio-common/printf-parse.h: Include string.h and wchar.h.
	(__find_specwc): Change into __extern_always_inline function.
	(__find_specmb): Likewise.  Remove ps argument.  Use __strchrnul.
	(__parse_one_specmb): Remove ps argument.
	* stdio-common/vfprintf.c (vfprintf): Remove mbstate variable.
	Adjust __find_specmb and __parse_one_specmb callers.
	* stdio-common/printf-prs.c (parse_printf_format): Likewise.
	* stdio-common/printf-parsemb.c (__find_specwc, __find_specmb):
	Removed.
	(__parse_one_specmb): Remove ps argument, adjust __find_specmb
	caller.

--- libc/stdio-common/printf-parse.h.jj	2003-06-12 00:05:01.000000000 +0200
+++ libc/stdio-common/printf-parse.h	2007-10-10 17:47:43.000000000 +0200
@@ -1,5 +1,6 @@
 /* Internal header for parsing printf format strings.
-   Copyright (C) 1995-1999, 2000, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1995-1999, 2000, 2002, 2003, 2007
+   Free Software Foundation, Inc.
    This file is part of th GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -20,6 +21,8 @@
 #include <printf.h>
 #include <stdint.h>
 #include <stddef.h>
+#include <string.h>
+#include <wchar.h>
 
 
 struct printf_spec
@@ -86,11 +89,17 @@ extern printf_function **__printf_functi
 
 /* Find the next spec in FORMAT, or the end of the string.  Returns
    a pointer into FORMAT, to a '%' or a '\0'.  */
-extern const unsigned char *__find_specmb (const UCHAR_T *format,
-					   mbstate_t *ps) attribute_hidden;
+__extern_always_inline const unsigned char *
+__find_specmb (const unsigned char *format)
+{
+  return (const unsigned char *) __strchrnul ((const char *) format, '%');
+}
 
-extern const unsigned int *__find_specwc (const UCHAR_T *format)
-     attribute_hidden;
+__extern_always_inline const unsigned int *
+__find_specwc (const unsigned int *format)
+{
+  return (const unsigned int *) __wcschrnul ((const wchar_t *) format, L'%');
+}
 
 
 /* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
@@ -100,8 +109,7 @@ extern const unsigned int *__find_specwc
    remains the highest argument index used.  */
 extern size_t __parse_one_specmb (const unsigned char *format, size_t posn,
 				  struct printf_spec *spec,
-				  size_t *max_ref_arg, mbstate_t *ps)
-     attribute_hidden;
+				  size_t *max_ref_arg) attribute_hidden;
 
 extern size_t __parse_one_specwc (const unsigned int *format, size_t posn,
 				  struct printf_spec *spec,
--- libc/stdio-common/vfprintf.c.jj	2007-07-29 11:45:15.000000000 +0200
+++ libc/stdio-common/vfprintf.c	2007-10-10 17:46:03.000000000 +0200
@@ -209,11 +209,6 @@ vfprintf (FILE *s, const CHAR_T *format,
   CHAR_T *workstart = NULL;
   CHAR_T *workend;
 
-  /* State for restartable multibyte character handling functions.  */
-#ifndef COMPILE_WPRINTF
-  mbstate_t mbstate;
-#endif
-
   /* We have to save the original argument pointer.  */
   va_list ap_save;
 
@@ -1294,11 +1289,8 @@ vfprintf (FILE *s, const CHAR_T *format,
   /* Find the first format specifier.  */
   f = lead_str_end = __find_specwc ((const UCHAR_T *) format);
 #else
-  /* Put state for processing format string in initial state.  */
-  memset (&mbstate, '\0', sizeof (mbstate_t));
-
   /* Find the first format specifier.  */
-  f = lead_str_end = __find_specmb ((const UCHAR_T *) format, &mbstate);
+  f = lead_str_end = __find_specmb ((const UCHAR_T *) format);
 #endif
 
   /* Lock stream.  */
@@ -1591,7 +1583,7 @@ vfprintf (FILE *s, const CHAR_T *format,
 #ifdef COMPILE_WPRINTF
       f = __find_specwc ((end_of_spec = ++f));
 #else
-      f = __find_specmb ((end_of_spec = ++f), &mbstate);
+      f = __find_specmb ((end_of_spec = ++f));
 #endif
 
       /* Write the following constant string.  */
@@ -1674,8 +1666,7 @@ do_positional:
 #ifdef COMPILE_WPRINTF
 	nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
 #else
-	nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg,
-				     &mbstate);
+	nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
 #endif
       }
 
--- libc/stdio-common/printf-prs.c.jj	2005-03-06 05:34:34.000000000 +0100
+++ libc/stdio-common/printf-prs.c	2007-10-10 17:44:54.000000000 +0200
@@ -1,5 +1,5 @@
-/* Copyright (C) 1991, 1992, 1995, 1996, 1999, 2000, 2002, 2003, 2004, 2005
-   Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1995, 1996, 1999, 2000, 2002, 2003, 2004, 2005,
+   2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -66,17 +66,16 @@ parse_printf_format (fmt, n, argtypes)
   size_t nargs;			/* Number of arguments.  */
   size_t max_ref_arg;		/* Highest index used in a positional arg.  */
   struct printf_spec spec;
-  mbstate_t mbstate;
   const unsigned char *f = (const unsigned char *) fmt;
 
   nargs = 0;
   max_ref_arg = 0;
 
   /* Search for format specifications.  */
-  for (f = __find_specmb (f, &mbstate); *f != '\0'; f = spec.next_fmt)
+  for (f = __find_specmb (f); *f != '\0'; f = spec.next_fmt)
     {
       /* Parse this spec.  */
-      nargs += __parse_one_specmb (f, nargs, &spec, &max_ref_arg, &mbstate);
+      nargs += __parse_one_specmb (f, nargs, &spec, &max_ref_arg);
 
       /* If the width is determined by an argument this is an int.  */
       if (spec.width_arg != -1 && (size_t) spec.width_arg < n)
--- libc/stdio-common/printf-parsemb.c.jj	2006-01-08 09:23:46.000000000 +0100
+++ libc/stdio-common/printf-parsemb.c	2007-10-10 17:44:25.000000000 +0200
@@ -46,35 +46,6 @@
 
 
 
-/* Find the next spec in FORMAT, or the end of the string.  Returns
-   a pointer into FORMAT, to a '%' or a '\0'.  */
-const UCHAR_T *
-#ifdef COMPILE_WPRINTF
-__find_specwc (const UCHAR_T *format)
-#else
-__find_specmb (const UCHAR_T *format, mbstate_t *ps)
-#endif
-{
-#ifdef COMPILE_WPRINTF
-  return (const UCHAR_T *) __wcschrnul ((const CHAR_T *) format, L'%');
-#else
-  while (*format != L_('\0') && *format != L_('%'))
-    {
-      int len;
-
-      /* Remove any hints of a wrong encoding.  */
-      ps->__count = 0;
-      if (! isascii (*format)
-	  && (len = __mbrlen ((const CHAR_T *) format, MB_CUR_MAX, ps)) > 0)
-	format += len;
-      else
-	++format;
-    }
-  return format;
-#endif
-}
-
-
 /* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
    with the parsed details.  POSN is the number of arguments already
    consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
@@ -87,8 +58,7 @@ __parse_one_specwc (const UCHAR_T *forma
 		    struct printf_spec *spec, size_t *max_ref_arg)
 #else
 __parse_one_specmb (const UCHAR_T *format, size_t posn,
-		    struct printf_spec *spec, size_t *max_ref_arg,
-		    mbstate_t *ps)
+		    struct printf_spec *spec, size_t *max_ref_arg)
 #endif
 {
   unsigned int n;
@@ -403,7 +373,7 @@ __parse_one_specmb (const UCHAR_T *forma
 #ifdef COMPILE_WPRINTF
       spec->next_fmt = __find_specwc (format);
 #else
-      spec->next_fmt = __find_specmb (format, ps);
+      spec->next_fmt = __find_specmb (format);
 #endif
     }
 

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]