On Fri, Feb 05, 2010 at 09:24:30PM -0500, Ted Unangst wrote: > 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.
Isn't it the task of the buffer cache to optimize memory use here? Or is the syscall overhead that large? Also, I wonder if this is fair wrt other processes doing I/O. -Otto > > 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) {