免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6126 | 回复: 11
打印 上一主题 下一主题

【提问】问一个关于用AIO库改善文件i/o性能的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-01-05 20:55 |只看该作者 |倒序浏览
本帖最后由 almeydifer 于 2011-01-05 20:58 编辑

各位,本人现在遇到了一个实际的问题(属于应用层):

1.一个第三方的应用程序首先用O_DIRECT标志位打开了一个比较大的镜像文件,大约是8GB。

2.此时我因为我某种需要,将该应用程序的写请求全部截获,并且存放在自己设计的一个缓存中,每一个写操作的相关的脏数据存放在一个结构体里,不同的写操作的结构体之间用链表来相连接。

3.在某个时刻,我需要重新调用write操作,将截获的写请求重新刷入磁盘中盖镜像文件对应的位置。

现在问题来了,如果直接调用write的话,非常慢,因为此文件使用O_DIRECT标志位打开,

所以呢,我想利用AIO,也就是Linux提供的异步I/O库来提升其刷入磁盘的性能,也就是写性能。我现在的问题代码片段如下:
  1. struct aiocb my_aiocb;
  2.        
  3. bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
  4.        
  5. my_aiocb.aio_fildes = prv->fd;

  6. list_for_each(_s, this_list){
  7.                 p = list_entry(_s, page_node_t, list);
  8.                 my_aiocb.aio_buf = malloc(p->size + 1);
  9.                 my_aiocb.aio_nbytes = p->size;
  10.                 my_aiocb.aio_offset = p->pageAddr;

  11.                 aio_write(&my_aiocb);
  12.         }
  13.        
  14.         while ( aio_error( &my_aiocb ) == EINPROGRESS );

  15.         return 1;
复制代码
中间那个list_for_each是将我前面提到的脏数据列表进行一个遍历,以刷新我所有缓存过的脏数据。
p是链表当中的结构体,pageaddr是我截获的写请求对应的offset,size是我截获的写请求对应的size
p当中还有个mybuf成员变量,用来缓存我截获的脏数据本身。
现在的问题是我感觉这个用法不大对劲,因为很可能我的用法不正确,
第一个问题是:性能几乎没有什么提高;
第二个问题是:狂占用内存,

各位如果有相关经验的话,恳请有劳指导一番。

论坛徽章:
0
2 [报告]
发表于 2011-01-05 23:04 |只看该作者
本帖最后由 kouu 于 2011-01-06 09:36 编辑

第二个问题:aio_buf的内存为什么要malloc呢?用完free了吗?

第一个问题:为什么aio_write要比write性能高?
我的理解是:
现在要做的事情是把数据写入磁盘,性能的瓶颈在于写磁盘。不管使用哪种系统调用,需要写的数据都是一样多的。从这个层面上说,aio_write比write没有优势。

不过,write是阻塞的,只能提交一块、等待写入磁盘、再提交下一块。而aio_write是不需要阻塞的,可以连续的把所有请求都提交了,再一起等待。这有什么好处呢?多次aio_write提交的请求就有可能得到合并,而如果请求得到合并,写盘效率势必会提高(请求合并减少了磁盘寻道的次数,而磁盘寻道又是写盘的瓶颈所在)。
那么,什么情况下两个请求能被合并呢?两个条件,一是两个请求的内存连续、二是两个请求的磁盘位置连续。这两个条件在LZ的测试代码中能满足吗?第一,内存连续的可能性不大,因为aio_buf是malloc出来的,malloc的内存是对齐的,且内存与内存之间还可能夹杂很多控制信息;而第二点,磁盘位置是否连续更是不可知的。所以,请求能够被合并的可能性实在很小。
从这个层面上说,aio_write比write也没有优势。

最后还有一个IO调度的问题,aio_write将所有请求连续提交了,内核可以使用类似电梯算法的IO调度算法来减少磁头移动的距离;而write则是提交一个处理一个,相当于先来先服务。从这个层面上说,aio_write应该的确有一定的优势。

论坛徽章:
0
3 [报告]
发表于 2011-01-06 08:27 |只看该作者
回复 2# kouu

谢谢kouu的回复,我仔细再找一下aio库的用法,我搜了一下网上的一些提示,google出来的文章90%都是IBM那个链接。没有很大的直接参考的意义。

我的用法可能还是有错误。

我先按照您的意见,去free看看。

论坛徽章:
0
4 [报告]
发表于 2011-01-06 10:56 |只看该作者
回复 1# almeydifer

各位,内存的占用率是下来了,但是速度还是很慢,我的现在的代码如下。
  1. list_for_each(_s, this_list){
  2.                 p = list_entry(_s, page_node_t, list);
  3.                 my_aiocb.aio_buf = p->buf;
  4.                 my_aiocb.aio_nbytes = p->size;
  5.                 my_aiocb.aio_offset = p->pageAddr;

  6.                 ret = aio_write(&my_aiocb);
  7.                
  8.                 if(ret < 0){
  9.                         mylog("aio_write error");
  10.                         return 0;
  11.                 }
  12.                
  13.                 ret = aio_error(&my_aiocb);
  14.                
  15.                 if (ret == 0){
  16.                
  17.                 } else if (ret == EINPROGRESS){
  18.                         if ( aio_suspend((const struct aiocb *const*)cblist, 1, NULL) ) {
  19.                                 mylog("aio_suspend error");
  20.                                 return 0;
  21.                         }
  22.                 } else {
  23.                         mylog("aio_error error");
  24.                 }
  25.                
  26.                 if (aio_return(&my_aiocb) != p->size){
  27.                         mylog("aio_return error:");
  28.                         return 0;
  29.                 }       
  30.         }
复制代码
我用的是aio_suspend,这个函数是不是非常影响性能,如果我把这段代码改成线程回调通知,会不会性能会有所改善?

论坛徽章:
0
5 [报告]
发表于 2011-01-08 21:33 |只看该作者
本帖最后由 almeydifer 于 2011-01-09 11:24 编辑

回复 2# kouu
现在俺又用了个歪办法,但是好像没有起到效果,
这个办法的原理就是用fctnl去除原本进程的O_DIRECT标志位,想让write系统调用能够利用文件系统的缓存来改善性能,但是失败了,性能还是很慢,相关代码如下:
  1. file_flag=fcntl(prv->fd,F_GETFL,0);
  2.         file_flag &= ~O_DIRECT;
  3.         fcntl(prv->fd,F_SETFL,file_flag);
  4.        
  5.         list_for_each( _s, this_list ) {
  6.                         p = list_entry(_s, page_node_t, list);
  7.                         lseek(prv->fd, p->pageAddr, SEEK_SET);
  8.                         write(prv->fd, p->buf, p->size);
  9.         }
  10.        
  11.         fcntl(prv->fd,F_GETFL,file_flag);
  12.         file_flag |= O_DIRECT;
  13.         fcntl(prv->fd,F_SETFL,file_flag);
复制代码

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
6 [报告]
发表于 2011-01-09 23:54 |只看该作者
回复  almeydifer

各位,内存的占用率是下来了,但是速度还是很慢,我的现在的代码如下。我用的是aio_s ...
almeydifer 发表于 2011-01-06 10:56



    aio_suspend ??
这段代码相当于是串行的,当然会慢。
应该使用信号通知或者回调函数的形式,而不是aio_suspend

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
7 [报告]
发表于 2011-01-09 23:57 |只看该作者
另外,如果全是同一个磁盘下的操作,那么性能还是在写磁盘上,这时aio_write()也是没有用的

论坛徽章:
0
8 [报告]
发表于 2011-01-10 11:28 |只看该作者
回复 7# chenzhanyiczy


    多谢您的回复,我马上试一试。

论坛徽章:
0
9 [报告]
发表于 2011-01-12 21:53 |只看该作者
回复 6# chenzhanyiczy

我现在用了基于回调函数的I/O,但是碰到个问题,就是在编译时,出现以下错误:

error: ‘struct sigevent’ has no member named ‘notify_function’

我当前用的2.6.18.8的Linux内核。
  1. my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
  2.                         (my_aiocb.aio_sigevent).notify_function = aio_completion_handler;
  3.                         (my_aiocb.aio_sigevent).notify_attributes = NULL;
  4.                         my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
复制代码

论坛徽章:
0
10 [报告]
发表于 2011-01-13 18:23 |只看该作者
回复 2# kouu

我现在把流程抠了出来,单独用了一个小程序来调试,但是发现用了基于回调函数的异步I/O后,数据不能够写入,不知道是原因,有谁愿意帮我看下吗?
代码我贴在下面:
  1. #include <stdio.h>
  2. #include <aio.h>
  3. #include <stdlib.h>
  4. #include <stdint.h>
  5. #include <string.h>


  6. void aio_completion_handler( sigval_t sigval);
  7. void setup_io(int fd, char *buf)
  8. {
  9.         struct aiocb my_aiocb;
  10.         int ret;


  11.         bzero( (char *)&my_aiocb, sizeof(struct aiocb) );
  12.        
  13.         my_aiocb.aio_fildes = fd;
  14.         my_aiocb.aio_buf = buf;
  15.         my_aiocb.aio_nbytes = 0;

  16.         my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
  17.         my_aiocb.aio_sigevent.sigev_notify_function = aio_completion_handler;
  18.         my_aiocb.aio_sigevent.sigev_notify_attributes = NULL;
  19.         my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;

  20.         ret = aio_write( &my_aiocb );
  21.         printf("send the aio request\n");
  22. }

  23. void aio_completion_handler( sigval_t sigval)
  24. {
  25.         struct aiocb *req;
  26.         int ret;
  27.         req = (struct aiocb *)sigval.sival_ptr;

  28.         if( aio_error(req) == 0)
  29.         {
  30.                 ret = aio_return(req);
  31.         }
  32.         printf("io done\n");
  33.         return;
  34. }

  35. int main()
  36. {
  37.         int fd;
  38.         char *buf;

  39.         buf = "123";
  40.         fd = open("/home/heihei.txt", O_CREAT | O_RDWR | O_APPEND , 77777);
  41.         setup_io(fd, buf);
  42.         sleep(10);
  43. }
复制代码
现在的问题是,“123”写不进文件“heihei.txt”, 终端只输出send the aio request, 而没有io done。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP