Though for a program like cp, this may qualify as a big diff. :)
Continuing in my "make IO suck less" phase, cp would be a lot more
efficient if it didn't bounce the disk heads around so much. Instead of
using a tiny 64k buffer, use an amount based on a small fraction of RAM.
Index: utils.c
===================================================================
RCS file: /home/tedu/cvs/src/bin/cp/utils.c,v
retrieving revision 1.30
diff -u -r1.30 utils.c
--- utils.c 27 Oct 2009 23:59:21 -0000 1.30
+++ utils.c 6 Feb 2010 01:46:42 -0000
@@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
+#include <sys/sysctl.h>
#include <err.h>
#include <errno.h>
@@ -46,29 +47,75 @@
#include "extern.h"
-int
-copy_file(FTSENT *entp, int dne)
+static void *
+allocbuf(size_t *sizep)
{
- static char *buf;
- static char *zeroes;
- struct stat to_stat, *fs;
- int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
-#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
- char *p;
-#endif
-
- if (!buf) {
- buf = malloc(MAXBSIZE);
- if (!buf)
- err(1, "malloc");
+ int mib[2];
+ uint64_t physmem;
+ void *buf;
+ size_t size = 0;
+ size_t oldlen;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM64;
+ oldlen = sizeof(physmem);
+ if (sysctl(mib, 2, &physmem, &oldlen, NULL, 0) == 0) {
+ size = physmem / 512;
+ size -= size % MAXBSIZE;
}
+ if (size < MAXBSIZE)
+ size = MAXBSIZE;
+ buf = malloc(size);
+ if (!buf)
+ err(1, "allocbuf(%ld)", size);
+
+ *sizep = size;
+ return buf;
+}
+
+static ssize_t
+writebuf(int to_fd, void *buf, ssize_t rcount, int skipholes)
+{
+ static void *zeroes;
+ ssize_t wcount = 0;
+ int amt = MAXBSIZE;
+ const char *bp = buf;
+ ssize_t left = rcount;
+
if (!zeroes) {
- zeroes = malloc(MAXBSIZE);
+ zeroes = calloc(1, amt);
if (!zeroes)
- err(1, "malloc");
- memset(zeroes, 0, MAXBSIZE);
+ err(1, "calloc");
+ }
+ while (left > 0) {
+ amt = MIN(amt, rcount);
+ if (skipholes && memcmp(bp, zeroes, amt) == 0)
+ wcount = lseek(to_fd, amt, SEEK_CUR) == -1 ? -1 : amt;
+ else
+ wcount = write(to_fd, bp, amt);
+ if (wcount != amt)
+ return -1;
+ bp += wcount;
+ left -= wcount;
}
+ return rcount;
+}
+
+int
+copy_file(FTSENT *entp, int dne)
+{
+ static void *buf;
+ static size_t bufsize;
+ struct stat to_stat, *fs;
+ int ch, checkch, from_fd, rval, to_fd;
+ size_t rcount, wcount;
+ int skipholes = 0;
+ struct stat tosb;
+
+ if (!buf)
+ buf = allocbuf(&bufsize);
+
if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
warn("%s", entp->fts_path);
return (1);
@@ -114,54 +161,21 @@
}
rval = 0;
-
- /*
- * Mmap and write if less than 8M (the limit is so we don't totally
- * trash memory on big files. This is really a minor hack, but it
- * wins some CPU back.
- */
-#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
- if (fs->st_size <= 8 * 1048576) {
- if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
- MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
- warn("mmap: %s", entp->fts_path);
- rval = 1;
- } else {
- madvise(p, fs->st_size, MADV_SEQUENTIAL);
- if (write(to_fd, p, fs->st_size) != fs->st_size) {
- warn("%s", to.p_path);
- rval = 1;
- }
- /* Some systems don't unmap on close(2). */
- if (munmap(p, fs->st_size) < 0) {
- warn("%s", entp->fts_path);
- rval = 1;
- }
- }
- } else
-#endif
- {
- int skipholes = 0;
- struct stat tosb;
- if (!fstat(to_fd, &tosb) && S_ISREG(tosb.st_mode))
- skipholes = 1;
- while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
- if (skipholes && memcmp(buf, zeroes, rcount) == 0)
- wcount = lseek(to_fd, rcount, SEEK_CUR) == -1 ?
-1 : rcount;
- else
- wcount = write(to_fd, buf, rcount);
- if (rcount != wcount || wcount == -1) {
- warn("%s", to.p_path);
- rval = 1;
- break;
- }
- }
- if (skipholes && rcount >= 0)
- rcount = ftruncate(to_fd, fs->st_size);
- if (rcount < 0) {
- warn("%s", entp->fts_path);
+ if (!fstat(to_fd, &tosb) && S_ISREG(tosb.st_mode))
+ skipholes = 1;
+ while ((rcount = read(from_fd, buf, bufsize)) > 0) {
+ wcount = writebuf(to_fd, buf, rcount, skipholes);
+ if (rcount != wcount) {
+ warn("%s", to.p_path);
rval = 1;
+ break;
}
+ }
+ if (skipholes && rcount >= 0)
+ rcount = ftruncate(to_fd, fs->st_size);
+ if (rcount < 0) {
+ warn("%s", entp->fts_path);
+ rval = 1;
}
if (rval == 1) {