我有一个C程序,它将32768个块写入,运行3.18.1内核的系统上的ext4文件系统,每个块大小为16K(总大小为512MB)。该程序的常规写入系统调用版本需要5.35秒来完成写入(由for循环之前和之后的gettimeofday测量)。然而,该程序的async io版本需要以下时间:
使用以下标志打开输出文件:O_WRONLY,O_CREAT,O_NONBLOCK
为什么异步io占用write()时间的两倍以上?即使是time-to-queue-async-io-request / time-to-write-sync-io也是1.4。
由于有些人将其标记为主题,我查看了定义并决定粘贴代码 - 这似乎是它应该被标记为主题的唯一原因。我不是在问为什么代码不起作用,只是为什么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);
}
#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);
}
答案 0 :(得分:1)
你并不是真的比较苹果和苹果。
在AIO代码中,每个写操作都有一个单独分配的缓冲区,因此程序分配了512 MiB(16 * 16 * 2 KiB)内存,以及AIO控制结构的32 K副本。必须分配,初始化该内存(如果set_buf()
函数(未显示),则每个缓冲区获取不同的值 - 将缓冲区的每个字节设置为第三个参数的值),然后由内核复制到驱动程序,可能通过内核缓冲池。
在常规IO代码中,您有一个大的,连续的缓冲区,它被初始化为您写入磁盘的所有零。
为了使比较公平,您应该在两个程序中使用相同的基础结构,创建AIO结构,但是常规IO代码将简单地逐步执行结构,编写每个结构的数据部分(而AIO代码将表现出来)或多或少如图所示)。而且我希望你会发现,当你这样做时,性能会更接近相似。