/* * Test performance of allocating a zeroed file via various methods on * various file systems * * Copyright (C) 2009 Red Hat Inc. * Author(s): Amit Shah * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #define GB(x) ((unsigned long long)x << 30) int pre_test_setup(char *source, char *target, char *fstype, unsigned long mntflags, char *mntopts, char *name, int *fd) { int r; if (source) { r = mount(source, target, fstype, mntflags, mntopts); if (r < 0) { perror("mount"); return -1; } } unlink(name); *fd = open(name, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); if (*fd < 0) { perror("open"); return -1; } /* Flush out all other data that might be pending writing to * the disk. We don't want to count the time we wait for the * buffers of other apps to clear. (Ideally, this test would * be run in single user mode with nothing else running on the * system.) */ sync(); sync(); /* Just to be sure - http://lwn.net/Articles/325420/ */ sync(); return 0; } int post_test_cleanup(char *target, int *fd) { int r; r = close(*fd); if (r < 0) perror("close"); if (strcmp(target, ".")) { r = umount(target); if (r < 0) perror("unmount"); } return r; } void run_test(int (test)(char *, int *, off_t, size_t), char *source, char *target, char *fstype, unsigned long mntflags, char *mntopts, char *filename, off_t len, size_t data) { int r, fd; r = pre_test_setup(source, target, fstype, mntflags, mntopts, filename, &fd); if (r < 0) goto error_exit; (test)(target, &fd, len, data); return; error_exit: post_test_cleanup(target, &fd); return; } int do_posix_fallocate(char *target, int *fd, off_t len, size_t data) { int r; struct timeval tv1, tv2; printf("posix-fallocate run time:\n"); gettimeofday(&tv1, NULL); r = posix_fallocate(*fd, 0, len); post_test_cleanup(target, fd); gettimeofday(&tv2, NULL); if (r < 0) { printf("posix_fallocate, error %d\n", r); return r; } printf("\tseconds:microseconds: %llu:%llu\n", tv1.tv_sec, tv1.tv_usec); printf("\tseconds:microseconds: %llu:%llu\n", tv2.tv_sec, tv2.tv_usec); printf("\t(approx %us)\n", tv2.tv_sec - tv1.tv_sec); return 0; } int do_fallocate(char *target, int *fd, off_t len, size_t data) { int r; struct timeval tv1, tv2; printf("fallocate run time:\n"); gettimeofday(&tv1, NULL); r = syscall(__NR_fallocate, *fd, 0, len); post_test_cleanup(target, fd); gettimeofday(&tv2, NULL); if (r < 0) { perror("fallocate"); return r; } printf("\tseconds:microseconds: %llu:%llu\n", tv1.tv_sec, tv1.tv_usec); printf("\tseconds:microseconds: %llu:%llu\n", tv2.tv_sec, tv2.tv_usec); printf("\t(approx %us)\n", tv2.tv_sec - tv1.tv_sec); return 0; } int do_mmap(char *target, int *fd, off_t len, size_t data) { struct timeval tv1, tv2; char *addr; /* memset has to have the mmap'ed file backed by something on * disk -- bus error otherwise */ ftruncate(*fd, len); addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, *fd, 0); if (addr == MAP_FAILED) { perror("mmap"); return -2; } printf("mmap run time:\n"); gettimeofday(&tv1, NULL); memset(addr, 0, len); munmap(addr, len); post_test_cleanup(target, fd); gettimeofday(&tv2, NULL); printf("\tseconds:microseconds: %llu:%llu\n", tv1.tv_sec, tv1.tv_usec); printf("\tseconds:microseconds: %llu:%llu\n", tv2.tv_sec, tv2.tv_usec); printf("\t(approx %us)\n", tv2.tv_sec - tv1.tv_sec); return 0; } int do_write_chunks(char *target, int *fd, off_t len, size_t data) { struct timeval tv1, tv2; unsigned long long remain = len; char *zeros; zeros = calloc(1, data); printf("%llu-sized chunk run time:\n", data); gettimeofday(&tv1, NULL); while (remain) { int bytes = data; if (bytes > remain) bytes = remain; if ((bytes = write(*fd, zeros, bytes)) < 0) { perror("write"); return -2; } remain -= bytes; } post_test_cleanup(target, fd); gettimeofday(&tv2, NULL); printf("\tseconds:microseconds: %llu:%llu\n", tv1.tv_sec, tv1.tv_usec); printf("\tseconds:microseconds: %llu:%llu\n", tv2.tv_sec, tv2.tv_usec); printf("\t(approx %us)\n", tv2.tv_sec - tv1.tv_sec); free(zeros); return 0; } int main(int argc, char **argv) { char *source, *target, *fstype, *mntopts; char filename[200]; unsigned long mntflags; unsigned long long filesize; /* FIXME! use getopt */ if (argc < 2 || argc > 2 && argc < 5) { printf("usage: %s [mntflags] [mntopts]\n", argv[0]); return -1; } filesize = GB(atol(argv[1])); if (argc > 2) { source = argv[2]; target = argv[3]; fstype = argv[4]; } else { source = NULL; target = "."; fstype = "native"; } if (argc > 5) mntflags = atol(argv[5]); else mntflags = 0; if (argc > 6) mntopts = argv[6]; else mntopts = NULL; sprintf(filename, "%s/%s-pf", target, fstype); run_test(do_posix_fallocate, source, target, fstype, mntflags, mntopts, filename, filesize, 0); #if 0 sprintf(filename, "%s/%s-fallocate", target, fstype); run_test(do_fallocate, source, target, fstype, mntflags, mntopts, filename, filesize, 0); #endif sprintf(filename, "%s/%s-mmap", target, fstype); run_test(do_mmap, source, target, fstype, mntflags, mntopts, filename, filesize, 0); sprintf(filename, "%s/%s-chunk4k", target, fstype); run_test(do_write_chunks, source, target, fstype, mntflags, mntopts, filename, filesize, 4 * 1024); sprintf(filename, "%s/%s-chunk8k", target, fstype); run_test(do_write_chunks, source, target, fstype, mntflags, mntopts, filename, filesize, 8 * 1024); return 0; }