Skip to content

Commit 55efc5f

Browse files
committed
cat,cp,mv,install,split: Set the minimum IO block size used to 32KiB
This is following on from this change: [02c3dc9 2008-03-06 cat: use larger buffer sizes ...] which increased the IO block size used by cat by 8 times, but also capped it at 32KiB. * NEWS: Mention the change in behavior. * src/system.h: Add a new io_blksize() function that returns the max of ST_BLKSIZE or 32KiB, as this was seen as a good value for a minimum block size to use to get good performance while minimizing system call overhead. * src/cat.c: Use it. * src/copy.c: ditto * src/split.c: ditto
1 parent 93f6771 commit 55efc5f

File tree

5 files changed

+58
-22
lines changed

5 files changed

+58
-22
lines changed

NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ GNU coreutils NEWS -*- outline -*-
2424
Previously -k1,1b would have caused leading space from field 2 to be
2525
included in the sort while -k2,3.0 would have not included field 3.
2626

27+
** Changes in behavior
28+
29+
cp,mv,install,cat,split: now read and write a minimum of 32KiB
30+
at a time. This was seen to increase throughput. Up to 2 times
31+
when reading cached files on linux for example.
2732

2833
* Noteworthy changes in release 7.1 (2009-02-21) [stable]
2934

src/cat.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@ static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;
7878
/* Preserves the `cat' function's local `newlines' between invocations. */
7979
static int newlines2 = 0;
8080

81-
static inline size_t
82-
compute_buffer_size (struct stat st)
83-
{
84-
return MIN (8 * ST_BLKSIZE (st), 32 * 1024);
85-
}
86-
8781
void
8882
usage (int status)
8983
{
@@ -642,7 +636,7 @@ main (int argc, char **argv)
642636
if (fstat (STDOUT_FILENO, &stat_buf) < 0)
643637
error (EXIT_FAILURE, errno, _("standard output"));
644638

645-
outsize = compute_buffer_size (stat_buf);
639+
outsize = io_blksize (stat_buf);
646640
/* Input file can be output file for non-regular files.
647641
fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
648642
on others, so the checking should not be done for those types,
@@ -706,7 +700,7 @@ main (int argc, char **argv)
706700
ok = false;
707701
goto contin;
708702
}
709-
insize = compute_buffer_size (stat_buf);
703+
insize = io_blksize (stat_buf);
710704

711705
/* Compare the device and i-node numbers of this input file with
712706
the corresponding values of the (output file associated with)

src/copy.c

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ copy_reg (char const *src_name, char const *dst_name,
568568
/* Choose a suitable buffer size; it may be adjusted later. */
569569
size_t buf_alignment = lcm (getpagesize (), sizeof (word));
570570
size_t buf_alignment_slop = sizeof (word) + buf_alignment - 1;
571-
size_t buf_size = ST_BLKSIZE (sb);
571+
size_t buf_size = io_blksize (sb);
572572

573573
/* Deal with sparse files. */
574574
bool last_write_made_hole = false;
@@ -596,21 +596,12 @@ copy_reg (char const *src_name, char const *dst_name,
596596
buffer size. */
597597
if (! make_holes)
598598
{
599-
/* These days there's no point ever messing with buffers smaller
600-
than 8 KiB. It would be nice to configure SMALL_BUF_SIZE
601-
dynamically for this host and pair of files, but there doesn't
602-
seem to be a good way to get readahead info portably. */
603-
enum { SMALL_BUF_SIZE = 8 * 1024 };
604-
605599
/* Compute the least common multiple of the input and output
606600
buffer sizes, adjusting for outlandish values. */
607601
size_t blcm_max = MIN (SIZE_MAX, SSIZE_MAX) - buf_alignment_slop;
608-
size_t blcm = buffer_lcm (ST_BLKSIZE (src_open_sb), buf_size,
602+
size_t blcm = buffer_lcm (io_blksize (src_open_sb), buf_size,
609603
blcm_max);
610604

611-
/* Do not use a block size that is too small. */
612-
buf_size = MAX (SMALL_BUF_SIZE, blcm);
613-
614605
/* Do not bother with a buffer larger than the input file, plus one
615606
byte to make sure the file has not grown while reading it. */
616607
if (S_ISREG (src_open_sb.st_mode) && src_open_sb.st_size < buf_size)

src/split.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ main (int argc, char **argv)
554554

555555
if (fstat (STDIN_FILENO, &stat_buf) != 0)
556556
error (EXIT_FAILURE, errno, "%s", infile);
557-
in_blk_size = ST_BLKSIZE (stat_buf);
557+
in_blk_size = io_blksize (stat_buf);
558558

559559
buf = ptr_align (xmalloc (in_blk_size + 1 + page_size - 1), page_size);
560560

src/system.h

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ enum
147147
# define D_INO(dp) NOT_AN_INODE_NUMBER
148148
#endif
149149

150+
/* include here for SIZE_MAX. */
151+
#include <inttypes.h>
152+
150153
/* Get or fake the disk device blocksize.
151154
Usually defined by sys/param.h (if at all). */
152155
#if !defined DEV_BSIZE && defined BSIZE
@@ -218,6 +221,51 @@ enum
218221
# endif
219222
#endif
220223

224+
/* As of Mar 2009, 32KiB is determined to be the minimium
225+
blksize to best minimize system call overhead.
226+
This can be tested with this script with the results
227+
shown for a 1.7GHz pentium-m with 2GB of 400MHz DDR2 RAM:
228+
229+
for i in $(seq 0 10); do
230+
size=$((8*1024**3)) #ensure this is big enough
231+
bs=$((1024*2**$i))
232+
printf "%7s=" $bs
233+
dd bs=$bs if=/dev/zero of=/dev/null count=$(($size/$bs)) 2>&1 |
234+
sed -n 's/.* \([0-9.]* [GM]B\/s\)/\1/p'
235+
done
236+
237+
1024=734 MB/s
238+
2048=1.3 GB/s
239+
4096=2.4 GB/s
240+
8192=3.5 GB/s
241+
16384=3.9 GB/s
242+
32768=5.2 GB/s
243+
65536=5.3 GB/s
244+
131072=5.5 GB/s
245+
262144=5.7 GB/s
246+
524288=5.7 GB/s
247+
1048576=5.8 GB/s
248+
249+
Note that this is to minimize system call overhead.
250+
Other values may be appropriate to minimize file system
251+
or disk overhead. For example on my current linux system
252+
the readahead setting is 128KiB which was read using:
253+
254+
file="."
255+
device=$(df -P --local "$file" | tail -n1 | cut -d' ' -f1)
256+
echo $(( $(blockdev --getra $device) * 512 ))
257+
258+
However there isn't a portable way to get the above.
259+
In the future we could use the above method if available
260+
and default to io_blksize() if not.
261+
*/
262+
enum { IO_BUFSIZE = 32*1024 };
263+
static inline size_t
264+
io_blksize (struct stat sb)
265+
{
266+
return MAX (IO_BUFSIZE, ST_BLKSIZE (sb));
267+
}
268+
221269
/* Redirection and wildcarding when done by the utility itself.
222270
Generally a noop, but used in particular for native VMS. */
223271
#ifndef initialize_main
@@ -228,8 +276,6 @@ enum
228276

229277
#include "timespec.h"
230278

231-
#include <inttypes.h>
232-
233279
#include <ctype.h>
234280

235281
#if ! (defined isblank || HAVE_DECL_ISBLANK)

0 commit comments

Comments
 (0)