aio_write比ext4上的普通写入花费更多时间?

时间:2015-01-27 12:04:54

标签: c

我有一个C程序,它将32768个块写入,运行3.18.1内核的系统上的ext4文件系统,每个块大小为16K(总大小为512MB)。该程序的常规写入系统调用版本需要5.35秒来完成写入(由for循环之前和之后的gettimeofday测量)。然而,该程序的async io版本需要以下时间:

  1. 排队所有aio_writes(32768 aio_writes):7.43秒
  2. 轮询完成每个IO请求:额外4.93秒
  3. 使用以下标志打开输出文件:O_WRONLY,O_CREAT,O_NONBLOCK

    为什么异步io占用write()时间的两倍以上?即使是time-to-queue-async-io-request / time-to-write-sync-io也是1.4。

    由于有些人将其标记为主题,我查看了定义并决定粘贴代码 - 这似乎是它应该被标记为主题的唯一原因。我不是在问为什么代码不起作用,只是为什么aio比常规写入慢得多,特别是因为所有并行写入都是针对不同的块。这是aio代码,后跟非aio代码:

    AIO计划

    #define MAX_AIO        (16384*2)
    #define BUFSIZE        16384
    
    struct mys {
        int status;
        struct aiocb aio;
    };
    
    void set_aiocb(struct mys *aio, int num, int fd)
    {
        int i;
    
        for (i = 0; i < num; i++) {
            aio[i].aio.aio_fildes = fd;
            aio[i].aio.aio_offset = BUFSIZE * i;
            aio[i].aio.aio_buf = malloc(BUFSIZE);
            set_buf(aio[i].aio.aio_buf, BUFSIZE, i);
            aio[i].aio.aio_nbytes = BUFSIZE;
            aio[i].aio.aio_reqprio = fd;
            aio[i].aio.aio_sigevent.sigev_notify = SIGEV_NONE;
            aio[i].aio.aio_sigevent.sigev_signo = SIGUSR1;
            aio[i].aio.aio_sigevent.sigev_value.sival_ptr = &aio[i];
            aio[i].aio.aio_lio_opcode = 0;
            aio[i].status = EINPROGRESS;
        }
    }
    
    void main(void)
    {
        int fd = open("/tmp/AIO", O_WRONLY | O_CREAT, 0666);
        int i, open_reqs = MAX_AIO;
        struct mys aio[MAX_AIO];
        struct timeval start, end, diff;
    
        set_aiocb(aio, MAX_AIO, fd);
    
        gettimeofday(&start, NULL);
        for (i = 0; i < MAX_AIO; i++)
            aio_write(&aio[i].aio);
    
        while (open_reqs > 0) {
            for (i = 0; i < MAX_AIO; i++) {
                if (aio[i].status == EINPROGRESS) {
                    aio[i].status = aio_error(&(aio[i].aio));
                    if (aio[i].status != EINPROGRESS)
                        open_reqs--;
                }
            }
        }
        gettimeofday(&end, NULL);
        timersub(&end, &start, &diff);
        printf("%d.%d\n", (int)diff.tv_sec, (int)diff.tv_usec);
    }
    

    常规IO程序

    #define MAX_AIO        (16384*2)
    #define BUFSIZE        16384
    
    char buf[MAX_AIO][BUFSIZE];
    void main(void)
    {
        int i, fd = open("/tmp/NON_AIO", O_WRONLY | O_CREAT, 0666);
        struct timeval start, end, diff;
    
        gettimeofday(&start, NULL);
        for (i = 0; i < MAX_AIO; i++)
            write(fd, buf[i], BUFSIZE);
        gettimeofday(&end, NULL);
        timersub(&end, &start, &diff);
        printf("%d.%d\n", (int)diff.tv_sec, (int)diff.tv_usec);
    }
    

1 个答案:

答案 0 :(得分:1)

你并不是真的比较苹果和苹果。

在AIO代码中,每个写操作都有一个单独分配的缓冲区,因此程序分配了512 MiB(16 * 16 * 2 KiB)内存,以及AIO控制结构的32 K副本。必须分配,初始化该内存(如果set_buf()函数(未显示),则每个缓冲区获取不同的值 - 将缓冲区的每个字节设置为第三个参数的值),然后由内核复制到驱动程序,可能通过内核缓冲池。

在常规IO代码中,您有一个大的,连续的缓冲区,它被初始化为您写入磁盘的所有零。

为了使比较公平,您应该在两个程序中使用相同的基础结构,创建AIO结构,但是常规IO代码将简单地逐步执行结构,编写每个结构的数据部分(而AIO代码将表现出来)或多或少如图所示)。而且我希望你会发现,当你这样做时,性能会更接近相似。