mmap に依る高速化を図った弊害2009年03月02日 17時05分32秒

現在の FreeBSD の cp コマンドには、8MB 以下のファイルの場合には mmap を使って複製するようになっている。以下の部分がその処理を行っている部分だ。

#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
        if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
            fs->st_size <= 8 * 1048576) {
            if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
                MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
                warn("%s", entp->fts_path);
                rval = 1;
            } else {
                wtotal = 0;
                for (bufp = p, wresid = fs->st_size; ;
                    bufp += wcount, wresid -= (size_t)wcount) {
                    wcount = write(to_fd, bufp, wresid);
                    if (wcount <= 0)
                        break;
                    wtotal += wcount;
                    if (info) {
                        info = 0;
                        (void)fprintf(stderr,
                            "%s -> %s %3d%%\n",
                            entp->fts_path, to.p_path,
                            cp_pct(wtotal, fs->st_size));
                    }
                    if (wcount >= (ssize_t)wresid)
                        break;
                }
                if (wcount != (ssize_t)wresid) {
                    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

以下の通り Makefile にて、この変数は有効にされている。

% cd /usr/src; find . -type f | xargs grep VM_AND_BUFFER_CACHE_SYNCHRONIZED
./bin/cp/Makefile:CFLAGS+= -DVM_AND_BUFFER_CACHE_SYNCHRONIZED
./bin/cp/utils.c:#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
./bin/cp/utils.c:#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
./bin/cp/utils.c:#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED

確かに、ローカルファイルを扱っている場合には速くなる。しかし、cp の様な一般的な汎用コマンドが、その様な特殊な場合だけに使われるわけがない。NFS などのネットワーク越しや、ローカルであっても、mmap が出来ないファイルシステムなどもある。

この様に汎用的なプログラムが、環境や使い方に依存する実装をとると大概にして墓穴を掘ることになる。実際に、mmap の所為で NFS での cp が 100 倍も遅くなってしまう。

汎用的に使いたいプログラム程、単純明解な実装にした方が良い。cp の場合は fread と fwrite の繰り返しなり、read と write の繰り返しだろう。特別な機能を加える度に、特殊な最適化を加える度に、自ら汎用性を落すことになる。