1
0
mirror of https://git.savannah.gnu.org/git/coreutils.git synced 2026-10-24 02:08:57 +03:00

uniq: avoid locking standard output when printing line counts with -c

This increases throughput on systems with unlocked stdio functions.
E.g., 2.5x in the following case:

    $ timeout 1m taskset 1 shuf -r -i 0-100 \
        | taskset 2 uniq -c \
        | taskset 3 pv -r > /dev/null
    [ 100MiB/s]
    $ timeout 1m taskset 1 shuf -r -i 0-100 \
        | taskset 2 uniq -c \
        | taskset 3 pv -r > /dev/null
    [ 246MiB/s]

* src/uniq.c: Include assure.h.
(writeline): Convert the value to a string by hand and use fwrite, which
may be unlocked, instead of printf.
* tests/uniq/uniq-c-width.sh: New test to verify the padding logic.
* tests/local.mk (all_tests): Add the test case.
* NEWS: Mention the improvement.
This commit is contained in:
Collin Funk
2026-06-25 20:12:28 -07:00
parent d7622d8629
commit 52ff9b97fb
4 changed files with 56 additions and 1 deletions

4
NEWS
View File

@@ -59,6 +59,10 @@ GNU coreutils NEWS -*- outline -*-
'sort' will now better use available memory and parallel operation
when reading from unknown sized inputs like pipes.
'uniq -c' now operates up to 2.5x times faster on systems with unlocked stdio
functions.
** Build-related
configure no longer accepts the --with-linux-crypto option, which allowed

View File

@@ -23,6 +23,7 @@
#include "system.h"
#include "argmatch.h"
#include "assure.h"
#include "linebuffer.h"
#include "fadvise.h"
#include "mcel.h"
@@ -324,7 +325,23 @@ writeline (struct linebuffer const *line,
return;
if (count_occurrences)
printf ("%7jd ", linecount + 1);
{
char buf[7 + INT_BUFSIZE_BOUND (intmax_t)];
char *end = buf + sizeof buf;
char *p = end;
*--p = ' ';
intmax_t i = linecount + 1;
affirm (0 < i);
do
*--p = '0' + i % 10;
while ((i /= 10));
/* 7 characters, padded with spaces if LINECOUNT + 1 is too small. */
while (end - p < 8)
*--p = ' ';
const idx_t nbytes = end - p;
if (fwrite (p, sizeof (char), nbytes, stdout) != nbytes)
write_error ();
}
if (fwrite (line->buffer, sizeof (char), line->length, stdout)
!= line->length)

View File

@@ -531,6 +531,7 @@ all_tests = \
tests/uniq/uniq.pl \
tests/uniq/uniq-perf.sh \
tests/uniq/uniq-collate.sh \
tests/uniq/uniq-c-width.sh \
tests/misc/xattr.sh \
tests/misc/yes.sh \
tests/tail/wait.sh \

33
tests/uniq/uniq-c-width.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/bin/sh
# Test 'uniq -c' with various integer widths.
# Copyright (C) 2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ uniq
expensive_
count=$(shuf -i 10000000-99999999 -n 1) || framework_failure_
while test $count -gt 0; do
printf '%7d y\n' $count >exp || framework_failure_
yes | head -n $count | uniq -c >out 2>err || fail=1
compare exp out || fail=1
compare /dev/null err || fail=1
count=$((count / 10))
done
Exit $fail