perf: defer locale, quote, and table setup until a count needs it
This commit is contained in:
+102
-9
@@ -78,6 +78,34 @@ static int posixly_correct = 0;
|
|||||||
static enum total_mode total_mode = TOTAL_AUTO;
|
static enum total_mode total_mode = TOTAL_AUTO;
|
||||||
static const char *files_from = NULL; /* --files0-from=F, or NULL */
|
static const char *files_from = NULL; /* --files0-from=F, or NULL */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LC_CTYPE handling. A freshly exec'd process already runs in the C
|
||||||
|
* locale, so when the environment selects C/POSIX (or selects nothing)
|
||||||
|
* the setlocale() call is skipped: glibc would only re-derive the same
|
||||||
|
* state, at a cost measurable on the 1-line benchmark inputs. When the
|
||||||
|
* locale really is multibyte we adopt it exactly like GNU does.
|
||||||
|
*/
|
||||||
|
static int in_c_locale; /* LC_CTYPE resolves to C/POSIX, or is unset */
|
||||||
|
static int locale_done; /* setup_locale() has run */
|
||||||
|
|
||||||
|
static void setup_locale(void)
|
||||||
|
{
|
||||||
|
const char *v;
|
||||||
|
|
||||||
|
if (locale_done)
|
||||||
|
return;
|
||||||
|
locale_done = 1;
|
||||||
|
v = getenv("LC_ALL");
|
||||||
|
if (v == NULL || v[0] == '\0')
|
||||||
|
v = getenv("LC_CTYPE");
|
||||||
|
if (v == NULL || v[0] == '\0')
|
||||||
|
v = getenv("LANG");
|
||||||
|
in_c_locale = v == NULL || v[0] == '\0' || strcmp(v, "C") == 0 ||
|
||||||
|
strcmp(v, "POSIX") == 0;
|
||||||
|
if (!in_c_locale)
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
}
|
||||||
|
|
||||||
/* gnulib quote() style: locale quotation marks in a UTF-8 locale,
|
/* gnulib quote() style: locale quotation marks in a UTF-8 locale,
|
||||||
* ASCII apostrophes elsewhere. Only argmatch diagnostics use it. */
|
* ASCII apostrophes elsewhere. Only argmatch diagnostics use it. */
|
||||||
static int curly_quotes;
|
static int curly_quotes;
|
||||||
@@ -141,6 +169,30 @@ static void init_isprint_tab(void)
|
|||||||
isprint_tab[i] = isprint((unsigned char)i) ? 1 : 0;
|
isprint_tab[i] = isprint((unsigned char)i) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The C/POSIX byte classes are fixed: isspace() is the six ASCII
|
||||||
|
* whitespace bytes and isprint() the range 0x20..0x7e. Precomputing
|
||||||
|
* them skips 512 ctype() calls per exec in the common C-locale run. */
|
||||||
|
static void init_ws_tab_c(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(ws_tab, 0, sizeof ws_tab);
|
||||||
|
for (i = '\t'; i <= '\r'; i++)
|
||||||
|
ws_tab[i] = 1;
|
||||||
|
ws_tab[' '] = 1;
|
||||||
|
ws_tab[0xa0] = (unsigned char)!posixly_correct;
|
||||||
|
nbsp_ws = ws_tab[0xa0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_isprint_tab_c(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(isprint_tab, 0, sizeof isprint_tab);
|
||||||
|
for (i = 0x20; i <= 0x7e; i++)
|
||||||
|
isprint_tab[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void try_help(void)
|
static void try_help(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Try 'fastwc --help' for more information.\n");
|
fprintf(stderr, "Try 'fastwc --help' for more information.\n");
|
||||||
@@ -187,9 +239,19 @@ static void qfile(FILE *out, const char *s)
|
|||||||
fputs(s, out);
|
fputs(s, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* quote(): always-quoting locale style used by argmatch diagnostics. */
|
/* quote(): always-quoting locale style used by argmatch diagnostics.
|
||||||
|
* The quote style is only needed on the --total error path, so the
|
||||||
|
* locale work stays out of every other invocation. */
|
||||||
static void qarg(FILE *out, const char *s)
|
static void qarg(FILE *out, const char *s)
|
||||||
{
|
{
|
||||||
|
static int qarg_ready;
|
||||||
|
|
||||||
|
if (!qarg_ready)
|
||||||
|
{
|
||||||
|
qarg_ready = 1;
|
||||||
|
setup_locale();
|
||||||
|
init_quote_style();
|
||||||
|
}
|
||||||
fputs(curly_quotes ? "\xe2\x80\x98" : "'", out);
|
fputs(curly_quotes ? "\xe2\x80\x98" : "'", out);
|
||||||
fputs(s, out);
|
fputs(s, out);
|
||||||
fputs(curly_quotes ? "\xe2\x80\x99" : "'", out);
|
fputs(curly_quotes ? "\xe2\x80\x99" : "'", out);
|
||||||
@@ -540,6 +602,34 @@ static count_lw_fn pick_kernel(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* One-time state the counting paths need, run after the options are
|
||||||
|
* known so --help/--version/usage errors pay none of it. The locale is
|
||||||
|
* only locale-sensitive for -m/-w/-L, the per-byte tables only for word
|
||||||
|
* and -L counting, and the SIMD kernel only for byte-path counts. */
|
||||||
|
static void init_count_state(void)
|
||||||
|
{
|
||||||
|
if (flags & (F_CHARS | F_WORDS | F_MAXLEN))
|
||||||
|
{
|
||||||
|
setup_locale();
|
||||||
|
if (flags & F_WORDS)
|
||||||
|
{
|
||||||
|
if (in_c_locale)
|
||||||
|
init_ws_tab_c();
|
||||||
|
else
|
||||||
|
init_ws_tab();
|
||||||
|
}
|
||||||
|
if (flags & F_MAXLEN)
|
||||||
|
{
|
||||||
|
if (in_c_locale)
|
||||||
|
init_isprint_tab_c();
|
||||||
|
else
|
||||||
|
init_isprint_tab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags & (F_LINES | F_WORDS))
|
||||||
|
count_lw = pick_kernel();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Unicode white space set as GNU wc 9.11 sees it: glibc's iswspace()
|
* The Unicode white space set as GNU wc 9.11 sees it: glibc's iswspace()
|
||||||
* (which omits the no-break spaces U+00A0, U+2007, U+202F) plus those
|
* (which omits the no-break spaces U+00A0, U+2007, U+202F) plus those
|
||||||
@@ -1413,16 +1503,10 @@ int main(int argc, char **argv)
|
|||||||
long n_args = 0;
|
long n_args = 0;
|
||||||
total_t totals = {0, 0, 0, 0, 0};
|
total_t totals = {0, 0, 0, 0, 0};
|
||||||
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
posixly_correct = getenv("POSIXLY_CORRECT") != NULL;
|
posixly_correct = getenv("POSIXLY_CORRECT") != NULL;
|
||||||
init_quote_style();
|
|
||||||
init_ws_tab();
|
|
||||||
init_isprint_tab();
|
|
||||||
count_lw = pick_kernel();
|
|
||||||
/* Line-buffer stdout like GNU, so parallel processes do not
|
|
||||||
* interleave their rows. */
|
|
||||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
|
||||||
|
|
||||||
|
/* Option parsing needs no locale, so --help/--version and usage
|
||||||
|
* errors return before any of the one-time startup state exists. */
|
||||||
while ((optc = getopt_long(argc, argv, "clLmw", longopts, NULL)) != -1)
|
while ((optc = getopt_long(argc, argv, "clLmw", longopts, NULL)) != -1)
|
||||||
switch (optc)
|
switch (optc)
|
||||||
{
|
{
|
||||||
@@ -1469,6 +1553,11 @@ int main(int argc, char **argv)
|
|||||||
if (flags == 0)
|
if (flags == 0)
|
||||||
flags = F_LINES | F_WORDS | F_BYTES; /* wc default: -l -w -c */
|
flags = F_LINES | F_WORDS | F_BYTES; /* wc default: -l -w -c */
|
||||||
|
|
||||||
|
/* Line-buffer stdout like GNU, so parallel processes do not
|
||||||
|
* interleave their rows. */
|
||||||
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
init_count_state();
|
||||||
|
|
||||||
if (files_from)
|
if (files_from)
|
||||||
{
|
{
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
@@ -1908,7 +1997,11 @@ int main(void)
|
|||||||
/* The CLI build's helpers are compiled out of reach here. */
|
/* The CLI build's helpers are compiled out of reach here. */
|
||||||
(void)usage;
|
(void)usage;
|
||||||
(void)try_help;
|
(void)try_help;
|
||||||
|
(void)setup_locale;
|
||||||
(void)init_isprint_tab;
|
(void)init_isprint_tab;
|
||||||
|
(void)init_isprint_tab_c;
|
||||||
|
(void)init_ws_tab_c;
|
||||||
|
(void)init_count_state;
|
||||||
(void)init_quote_style;
|
(void)init_quote_style;
|
||||||
(void)qaf;
|
(void)qaf;
|
||||||
(void)qfile;
|
(void)qfile;
|
||||||
|
|||||||
Reference in New Issue
Block a user