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) {

Reply via email to