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:
4
NEWS
4
NEWS
@@ -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
|
||||
|
||||
19
src/uniq.c
19
src/uniq.c
@@ -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)
|
||||
|
||||
@@ -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
33
tests/uniq/uniq-c-width.sh
Executable 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
|
||||
Reference in New Issue
Block a user