http://no001.blog.51cto.com/1142339/1107776 2013 关于Unix/Linux环境下的文件操作对文件进行操作有两种方式:一种是直接使用unix api, 一种是使用标准C库。 我对这两种方法执行效率进行了比较。 实验内容分为3个部分: 1 读数据小路比较 2 打开文件效率比较 3 linux环境进程支持的最大打开文件数 首先介绍一下实验环境和使用的工具: Unbuntu Dapper Drake 6.06 gcc 4.0.3 使用的工具有: time, 输出重定向 time 程序名(命令) 参数 < 重定向文件 time的输出: real 程序运行的实际时间 user 程序运行用户态代码使用时间 sys 程序运行系统态代码使用时间 实验数据为一个 50M大小的文件. 1 读文件的比较 unix api方式: #include <unistd.h> int read(fd, buf, size); fd是文件描述符号,在unistd.h中预定义了STDIN_FILENO等标准文件描述符。 buf是char型数组 size是每次读入的缓冲区大小 返回值是实际读到字节数, 在接近文件尾的时候,返回值可能小于size的 在文件尾的时候返回0 出错返回负数 read必须自己定义缓冲区的大小,如果定义不当,可能导致效率降低。 缓冲区的定义,也不适越大越好。缓冲区的大小最好与磁盘的块大小保持一致。 在<advanced programming in the UNIX enviroment>给出的数据中:块的大小为4096. 实验代码和结果: #define BUFFSIZE 4096 #define G_BUFF 1073741824 // 2^30 #define M_BUFF 1048576 // 2^20 #define K_BUFF 1024 // 2^10 int n; char buf[M_BUFF]; char c; while ((n=(read(STDIN_FILENO, buf, BUFFSIZE) ) ) >0) { for (int i=0;i<n;i++) { c=buf[i]; } } root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m3.835s user 0m0.112s sys 0m0.079s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m3.436s user 0m0.126s sys 0m0.085s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.790s user 0m0.143s sys 0m0.031s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.215s user 0m0.134s sys 0m0.034s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.182s user 0m0.143s sys 0m0.021s 实验结果分析 可以看到开始运行的时候执行时间比较长。 我的分析是,由于我分配了1M的内存。系统调整内存分配需要一段时间。 当重复运行几次后,效果就非常好了。 顺带说一下write操作,原型和read基本一致. 返回值是实际写的字节数。如果与缓冲区的大小不一致,说明写错处。 标准C方式1 实验代码和结果 int c; while ((c=getc(stdin))!=EOF) { } root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m1.433s user 0m1.057s sys 0m0.030s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m1.128s user 0m1.048s sys 0m0.025s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m1.100s user 0m1.048s sys 0m0.023s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m1.113s user 0m1.058s sys 0m0.018s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m1.162s user 0m1.045s sys 0m0.028s 标准C语言方式2 实验代码和结果 char c; while (scanf("%c",&c)>0) { ; } root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m4.938s user 0m4.831s sys 0m0.032s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m4.845s user 0m4.742s sys 0m0.029s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m5.224s user 0m5.118s sys 0m0.028s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m5.300s user 0m5.188s sys 0m0.028s 标准C语言方式3 实验代码和结果 int n; char buf[BUFFSIZE]; char c; while ( (n=fread(buf,sizeof(char), BUFFSIZE,stdin) ) >0) { for (int i=0;i<n;i++) { c=buf[i]; } } if (feof(stdin)) { printf("eof"); } if (ferror(stdin)) { printf("error"); } root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.171s user 0m0.133s sys 0m0.032s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.177s user 0m0.143s sys 0m0.022s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.175s user 0m0.152s sys 0m0.015s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.176s user 0m0.147s sys 0m0.017s root@zhongcun:~/c++/forXunLei# g++ unixIO.cpp root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.172s user 0m0.136s sys 0m0.028s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.172s user 0m0.131s sys 0m0.032s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.172s user 0m0.146s sys 0m0.019s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.173s user 0m0.138s sys 0m0.027s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m0.172s user 0m0.136s sys 0m0.029s Unix API 不使用缓存的unix api 机器声音很大,猜测是读取硬盘的时候发出的声音. int n; char buf[M_BUFF]; char c; while ((n=(read(STDIN_FILENO, buf, 1) ) ) >0) { for (int i=0;i<n;i++) { c=buf[i]; } } root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m30.612s user 0m4.503s sys 0m26.083s root@zhongcun:~/c++/forXunLei# time ./a.out <infile 0 real 0m30.658s user 0m4.613s sys 0m26.021s 标准C语言函数 不实用缓存的fread int n; char buf[BUFFSIZE]; char c; while ( (n=fread(buf,sizeof(char), 1,stdin) ) >0) { for (int i=0;i<n;i++) { c=buf[i]; } } root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m4.029s user 0m3.990s sys 0m0.030s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m4.034s user 0m3.999s sys 0m0.025s root@zhongcun:~/c++/forXunLei# time ./a.out <infile real 0m4.121s user 0m4.017s sys 0m0.032s 实验结论 需要处理文件中每个字符的时候 单位(s) read getc scanf fread read(no buf) fread(no buf) user 0.13 1.04 4.9 0.14 4.5 4.0 sys 0.03 0.02 0.02 0.03 26 0.03 可以看到 getc, scanf, fread由于使用了std io自带的buffer, 所以系统的开销始终维持在0.03。 而read如果对buf的选择不当,系统调用时间可能很长。 然后是用户态代码执行时间: 带buff的read, fread是最快的。 但不带buf的执行时间是带buf的40倍。 而且要比getc的执行时间还要慢。 分析原因, 将数据拷贝到buf内需要时间。执行for循环体内的字符赋值语句需要时间。 getc之所以慢,std io从缓冲区取数据是需要时间的。 scanf之所以慢,格式化的操作是需要时间的。 推荐: 如果是针对字符串的读写,推荐使用fread。 因为是标准C类库的文件,可移植性高。 而且如果使用buffer,效率和系统调用不相上下. <advanced programming in unix enviroment> 中对提到的fread的不足: 1 The offset of a member within a structure can differ between compilers and systems, because of different aliagnment requirements. 2 The binary format used to store multibyte integers and floating-point values differ among machine architectures. 2 打开文件操作 系统调用和std io的差别不适很大 打开关闭1,000,000次 用户态代码平均时间0.2s 系统态代码平均时间1.7s for (int i=0;i<M_BUFF;i++) { int fd=open ("infile", O_RDONLY, "r"); close(fd); } root@zhongcun:~/c++/forXunLei# time ./a.out real 0m1.947s user 0m0.202s sys 0m1.689s root@zhongcun:~/c++/forXunLei# time ./a.out real 0m2.012s user 0m0.213s sys 0m1.745s root@zhongcun:~/c++/forXunLei# time ./a.out real 0m1.934s user 0m0.203s sys 0m1.670s for (int i=0;i<M_BUFF;i++) { FILE * fp =fopen("infile", "r"); fclose(fp); } root@zhongcun:~/c++/forXunLei# time ./a.out real 0m2.384s user 0m0.783s sys 0m1.549s root@zhongcun:~/c++/forXunLei# time ./a.out real 0m2.409s user 0m0.783s sys 0m1.573s 3 进程所能打开的最大文件 char files[K_BUFF*3/2][3]; for (int i=0;i<K_BUFF*3/2;i++) { files[i][0]=i+1; files[i][1]=i+2; files[i][2]='\0'; } for (int i=0;i<K_BUFF*3/2;i++) { int fd=creat (files[i], O_WRONLY); printf("opened %s, fd=%d\n",files[i], fd); } 实验代码2 for (int i=0;i<1025;i++) { int fd=open ("infile", O_RDONLY,"r"); printf("opened infile, fd=%d\n", fd); } 结果分析: fd的取值范伟从3~1023 0, 1, 2表示标准输入,输出,错误 1023之后fd输出全部为-1 -1表示,返回一个错误 这个最大可以打开的值应该是平台相关的 杂悟: 1 小错误 prinf无定义 printf create无定义 creat 2 其他搜索 groff time分析程序 3 系统总结std io函数的用法<stdio.h> 系统总结unix api函数的用法<fcntl.h><unistd.h> 4 关于std io实现的思考 这取决于编译器堆,类库的实现 是否是直接调用os api呢? 这是最容易的实现 但intel开发的编译器?他调用哪个操作系统的api? 编译器将代码转化为指令 需要对语言标准进行支持,对标准类库进行支持。 5 man time 6 当系统运行很多大程序(OpenOffice Word)的时候,执行时间显著变慢。 7 标准声明的变量 #include <stdio.h> stdio是在该文件中定义的文件指针。 EOF std io对读错误和文件结束的处理 read直接可以通过返回的整数来确认 但文件是否出错或到达末尾需要通过函数feof(), ferror()来检测 |
|
来自: 心不留意外尘 > 《linux op》