Почему fsync () занимает намного больше времени в ядре Linux 3.1. *, Чем в ядре 3.0 - PullRequest
7 голосов
/ 16 января 2012

У меня есть тестовая программа.В ядре Linux 3.1. * Это занимает около 37 секунд, а в ядре 3.0.18 - около 1 секунды (я просто заменяю ядро ​​на той же машине, что и раньше).Пожалуйста, дайте мне подсказку, как улучшить его в ядре 3.1.Спасибо!

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int my_fsync(int fd)
{
    // return fdatasync(fd);
    return fsync(fd);
}


int main(int argc, char **argv)
{
    int rc = 0;
    int count;
    int i;
    char oldpath[1024];
    char newpath[1024];
    char *writebuffer = calloc(1024, 1);

    snprintf(oldpath, sizeof(oldpath), "./%s", "foo");
    snprintf(newpath, sizeof(newpath), "./%s", "foo.new");

    for (count = 0; count < 1000; ++count) {
    int fd = open(newpath, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU);
    if (fd == -1) {
        fprintf(stderr, "open error! path: %s\n", newpath);
        exit(1);
    }

    for (i = 0; i < 10; i++) {
        rc = write(fd, writebuffer, 1024);
        if (rc != 1024) {
        fprintf(stderr, "underwrite!\n");
        exit(1);
        }
    }

    if (my_fsync(fd)) {
        perror("fsync failed!\n");
        exit(1);
    }

    if (close(fd)) {
        perror("close failed!\n");
        exit(1);
    }

    if (rename(newpath, oldpath)) {
        perror("rename failed!\n");
        exit(1);
    }

    }

    return 0;
}


# strace -c ./testfsync
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 98.58    0.068004          68      1000           fsync
  0.84    0.000577           0     10001           write
  0.40    0.000275           0      1000           rename
  0.19    0.000129           0      1003           open
  0.00    0.000000           0         1           read
  0.00    0.000000           0      1003           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         2           setitimer
  0.00    0.000000           0        68           sigreturn
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           mprotect
  0.00    0.000000           0         2           writev
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         6           mmap2
  0.00    0.000000           0         2           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.068985                 14099         1 total

Ответы [ 2 ]

8 голосов
/ 16 января 2012

Ядро 3.1. * Фактически выполняет синхронизацию, 3.0.18 притворяется.Ваш код делает 1000 синхронизированных записей.Поскольку вы усекаете файл, каждая запись также увеличивает его.Таким образом, у вас на самом деле 2000 операций записи.Типичная задержка записи на жесткий диск составляет около 20 миллисекунд на ввод / вывод.Таким образом, 2000 * 20 = 40000 миллисекунд или 40 секунд.Так что кажется правильным, если вы пишете на типичный жесткий диск.

По сути, синхронизируя после каждой записи, вы не даете ядру возможности эффективно кэшировать или перекрывать записи и вызывать поведение в худшем случае.на каждой операции.Кроме того, на жестком диске приходится искать между тем, где записываются данные, и где метаданные записываются один раз для каждой записи.

2 голосов
/ 17 января 2012

Нашел причину.Барьеры файловой системы по умолчанию включены в ext3 для ядра Linux 3.1 (http://kernelnewbies.org/Linux_3.1). После отключения барьеров он становится намного быстрее.

...