From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 25543 invoked by alias); 10 Oct 2007 19:28:51 -0000 Received: (qmail 25518 invoked by uid 22791); 10 Oct 2007 19:28:51 -0000 X-Spam-Check-By: sourceware.org Received: from sunsite.ms.mff.cuni.cz (HELO sunsite.mff.cuni.cz) (195.113.15.26) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 10 Oct 2007 19:28:44 +0000 Received: from sunsite.mff.cuni.cz (localhost.localdomain [127.0.0.1]) by sunsite.mff.cuni.cz (8.13.8/8.13.8) with ESMTP id l9AJVjgp015216; Wed, 10 Oct 2007 21:31:45 +0200 Received: (from jj@localhost) by sunsite.mff.cuni.cz (8.13.8/8.13.8/Submit) id l9AJVjdu015215; Wed, 10 Oct 2007 21:31:45 +0200 Date: Wed, 10 Oct 2007 19:28:00 -0000 From: Jakub Jelinek To: Ulrich Drepper Cc: Glibc hackers Subject: [PATCH] __find_specmb tweaks Message-ID: <20071010193145.GI2896@sunsite.mff.cuni.cz> Reply-To: Jakub Jelinek Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.4.2.2i Mailing-List: contact libc-hacker-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-hacker-owner@sourceware.org X-SW-Source: 2007-10/txt/msg00011.txt.bz2 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 #include 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 #include 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 #include 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 #include 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 #include 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 #include 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 * 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 #include #include +#include +#include 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