zoukankan      html  css  js  c++  java
  • read()/fread()/mmap()执行效率对比

    一、 read()/fread()/mmap()执行效率对比

    系统调用read.c:
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main()
    {
        int fd = open("linux_logo.pnm", O_RDONLY);
        int num = 0;
        char buff[1];
        
        do{
            num = read(fd, buff, 1);
        }while(num != 0);
        
        close(fd);
        
        return 0;
    }
    库函数fread.c:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        FILE *fp = fopen("linux_logo.pnm", "r");
        int num = 0;
        char buff[1];
        
        do{
            num = fread(buff, 1, 1, fp);
        }while(num != 0);
        
        close(fp);
        
        return 0;
    }
    使用mmap进行文件映射:
    #include <fcntl.h>      //open
    #include <sys/mman.h>   //mmap
    #include <sys/types.h>  //fstat
    #include <sys/stat.h>
    #include <unistd.h>
    
    int main()
    {
        int i;
        char buff[1];
        struct stat statue;
        
        int fd = open("linux_logo.pnm", O_RDONLY);
        
        fstat(fd, &statue);
    
        char *pm = mmap(NULL, statue.st_size, PROT_READ, MAP_SHARED, fd, 0) ;
    
        for(i=0; i<statue.st_size; i++)
        {
            buff[0] = pm[i];
        }
    
        munmap(pm, statue.st_size);
    
        close(fd);
        
        return 0;
    }
    编译后三者执行时间:
    time ./read
    real    0m2.830s
    user    0m0.287s
    sys    0m2.178s
    
    time ./fread
    real    0m0.088s
    user    0m0.082s
    sys    0m0.008s
    
    time ./mmap
    real    0m0.026s
    user    0m0.008s
    sys    0m0.005s
    
    ll linux_logo.pnm 
    -rw-r--r--. 1 root root 1152015 Aug 27 15:10 linux_logo.pnm

    1. ./read的sys(内核态时间)是./fread的2.178/0.008=272倍,而1152015/4096(细页大小为4kB)=281,非常接近,由知识背景推测出可能./read进行的系统调用次数为./fread的4096倍(因为fread()是带缓冲的,每次可能读取1页数据缓存起来,而read()不带缓冲,每次都从硬盘重新读取)

    证明:

    strace ./read 打印信息如下:

    read(3, "375", 1) = 1
    read(3, "375", 1) = 1
    read(3, "375", 1) = 1
    read(3, "375", 1) = 1
    read(3, ^C <unfinished ...>

    strace ./fread 打印信息如下:

    read(3, "=211+=210+=206+<201+8200/8m%)Y3131b)"tB7{QAs"..., 4096) = 4096
    read(3, "223234211223234211223234211224235212225236213230241214227240213225236211224235"..., 4096) = 4096
    read(3, "YHzYHxWFuTCvRBvRBuP>rM;uR?uR?sP="..., 4096) = 4096
    read(3, "Z231|\227zZ226y[231|^232177a232177a232201c237206h245214n250217q247"..., 4096) = 1039
    read(3, "", 4096) = 0
    exit_group(0) = ?
    [root@sfl sys_call]# ^C

    可以看出./read每次执行read系统调用都只读取1字节,而./fread中的fread()的read系统调用每次读取4096字节,证明了自己的猜测。

    2.在频繁读取问价的情况下,mmap()进行文件映射时执行效率最高的,因为它不需要频繁进行系统调用,可以像操作内存一样操作文件。

    3.系统调用是相当耗费时间的,注意适当使用系统调用。

    二、相关命令学习

    1.time(测量)命令

    time命令常用于测量一个命令的运行时间,注意不是用来显示和修改系统时间的(这是date命令干的事情)

    real    0m5.064s      <== 实际使用时间(real time,算上了等待、休眠等的时间)
    user    0m0.020s     <== 用户态使用时间(the process spent in user mode)
    sys     0m0.040s      <== 内核态使用时间(the process spent in kernel mode)

    man time后根据说明带参数操作是不成功的,原因是:这个time使用的是shell脚本里面的,是不带命令行参数的,而可执行程序是带命令行参数的

    type -a time                    /*列出time命令的类型,发现有两种*/

    time is a shell keyword   /*shell脚本中也提供了这个命令,而且默认是使用shell脚本中的*/

    time is /usr/bin/time       /*命令行程序*/

      使用可执行程序的time即可带命令行参数,标识将./read的执行写入到test.txt中而不是command窗口,-v友好的显示;注意这里的time是测量的意思,还会列出很多其他的信息。

    /usr/bin/time -o test.txt -v ./read

      无法将time的输出信息重定向到文件里面,为什么?因为time是shell的关键字,shell做了特殊处理,它会把time命令后面的命令行作为一个整体来进行处理,在重定向时,实际上是针对后面的命令来的,time命令本身的输出并不会被重定向的,两种解决方法:

    { time command-line; } 2>file  注意分隔符的使用。

    (time command-line) 2>file 这里time紧贴着小括号(也可以的,命令行结束也不必带分号。

    2.strace命令

    作用:在最简单的情况下,strace运行指定的命令,直到它退出。它拦截并记录由进程调用的系统调用和进程接收到的信号。 每个系统调用的名称,其参数及其返回值都以标准错误或-o选项指定的文件打印。

    文件hello.c
    
    #include <stdio.h>
    
    void main()
    {
        printf("hello world!
    ");
    }
    
    gcc hello.c -o hello

    执行命令:strace ./hello 打印如下信息:

    execve("./pp", ["./pp"], [/* 57 vars */]) = 0
    brk(0)                                  = 0x9dfe000
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb780b000
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY)      = 3
    fstat64(3, {st_mode=S_IFREG|0644, st_size=126040, ...}) = 0
    mmap2(NULL, 126040, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77ec000
    close(3)                                = 0
    open("/lib/libc.so.6", O_RDONLY)        = 3
    read(3, "177ELF111331@236201004"..., 512) = 512
    fstat64(3, {st_mode=S_IFREG|0755, st_size=1876456, ...}) = 0
    mmap2(0x803000, 1636744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x803000
    mprotect(0x98c000, 4096, PROT_NONE)     = 0
    mmap2(0x98d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x189) = 0x98d000
    mmap2(0x990000, 10632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x990000
    close(3)                                = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77eb000
    set_thread_area({entry_number:-1 -> 6, base_addr:0xb77eb6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
    mprotect(0x98d000, 8192, PROT_READ)     = 0
    mprotect(0x7fb000, 4096, PROT_READ)     = 0
    munmap(0xb77ec000, 126040)              = 0
    fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb780a000
    write(1, "hello world!
    ", 13)          = 13
    exit_group(13)                          = ?

    现在知道为什么包含glibc中相应的头文件就能使用里面的函数了吧,它在库/lib/libc.so.6里面,包含头文件只是为了能通过编译期而已。

    参考:http://www.cnblogs.com/syntax/archive/2012/11/05/2755129.html

  • 相关阅读:
    Java静态类
    【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期
    【Java TCP/IP Socket】深入剖析socket——TCP通信中由于底层队列填满而造成的死锁问题(含代码)
    【Java TCP/IP Socket】深入剖析socket——数据传输的底层实现
    【Java TCP/IP Socket】基于NIO的TCP通信(含代码)
    【Java TCP/IP Socket】Java NIO Socket VS 标准IO Socket
    【Java TCP/IP Socket】TCP Socket通信中由read返回值造成的的死锁问题(含代码)
    数据结构课后练习题(练习三)7-5 Tree Traversals Again (25 分)
    快速排序详解(lomuto划分快排,hoare划分快排,classic经典快排,dualpivot双轴快排源码)
    Java多线程(一)——线程基础和锁锁锁
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/7440702.html
Copyright © 2011-2022 走看看