C中fgetc / fputc和fread / fwrite之间的速度比较

所以(只是为了好玩),我只是想编写一个C代码来复制文件。 我四处看看似乎所有从流中读取的函数调用fgetc() (我希望这是真的吗?),所以我使用了这个函数:

 #include  #include  #include  #define FILEr "img1.png" #define FILEw "img2.png" main() { clock_t start,diff; int msec; FILE *fr,*fw; fr=fopen(FILEr,"r"); fw=fopen(FILEw,"w"); start=clock(); while((!feof(fr))) fputc(fgetc(fr),fw); diff=clock()-start; msec=diff*1000/CLOCKS_PER_SEC; printf("Time taken %d seconds %d milliseconds\n", msec/1000, msec%1000); fclose(fr); fclose(fw); } 

在2.10Ghz core2Duo T6500 Dell inspiron笔记本电脑上, 这个文件的运行时间为140毫秒。 但是,当我尝试使用fread / fwrite ,我会减少运行时间,因为我不断增加为每个调用传输的字节数(即,以下代码中的变量st ),直到它在10ms左右达到峰值! 这是代码:

 #include  #include  #include  #define FILEr "img1.png" #define FILEw "img2.png" main() { clock_t start,diff; // number of bytes copied at each step size_t st=10000; int msec; FILE *fr,*fw; // placeholder for value that is read char *x; x=malloc(st); fr=fopen(FILEr,"r"); fw=fopen(FILEw,"w"); start=clock(); while(!feof(fr)) { fread(x,1,st,fr); fwrite(x,1,st,fw); } diff=clock()-start; msec=diff*1000/CLOCKS_PER_SEC; printf("Time taken %d seconds %d milliseconds\n", msec/1000, msec%1000); fclose(fr); fclose(fw); free(x); } 

为什么会这样? 即如果fread实际上是多次调用fgetc然后为什么速度差? 编辑:指定“增加字节数”是指第二个代码中的变量st

fread()不会调用fgetc()来读取每个字节。

它的行为就像重复调用fgetc() ,但它可以直接访问fgetc()从中读取的缓冲区,因此可以直接复制更大量的数据。

您忘记了文件缓冲( inode,dentry和页面缓存 )。

在运行之前清除它们:

 echo 3 > /proc/sys/vm/drop_caches 

背景资料:

基准测试是一门艺术。 请参阅bonnie++iozonephoronix以进行适当的文件系统基准测试。 作为一个特征, bonnie++不允许使用小于可用系统内存2倍的写入量的基准测试。

为什么?

(回答:缓冲效果!)

就像sehe所说的部分原因是缓冲,但还有更多内容,我会解释为什么会这样,同样为什么fgetc()会给出更多的延迟。

为从文件读取的每个字节调用fgetc()

为文件数据的本地缓冲区的每n个字节调用fread()

所以对于10MiB文件:

fgetc()被称为:10 485 760次

虽然使用1KiB缓冲器进行预防,但该function称为10 240次。

让我们说简单,每个函数调用需要1ms: fgetc需要10 485 760 ms = 10485.76秒〜2,9127小时fread需要10 240 ms = 10.24秒

最重要的是操作系统通常在同一设备上进行读写操作,我想你的例子是在同一个硬盘上进行的。 操作系统在读取源文件时,将硬盘磁头移动到寻找文件的旋转磁盘盘上,然后读取1个字节,将其放在内存中,然后将读/写磁头再次移动到硬盘旋转盘上查看该位置操作系统和硬盘控制器同意找到目标文件,然后从内存写入1个字节。 对于上面的例子,每个文件发生超过1000万次:总计超过2000万次,使用缓冲版本,总共发生超过20 000次。

除了操作系统在读取磁盘时为了性能目的而将更多的KiB硬盘数据放入内存中,这样即使使用效率较低的fgetc也可以加速程序,因为程序从OS内存中读取而不是直接从硬盘。 这是sehe的回应所指的。

根据您的机器配置/负载/操作系统/等,您的读取和写入结果可能会有很大差异,因此他建议清空磁盘缓存以获得更有意义的结果。

当源文件和目标文件在不同的硬盘上时,事情要快得多。 对于SDD,我不确定读/写是否完全相互排斥。

简介:对函数的每次调用都有一定的开销,从HDD读取有其他开销,缓存/缓冲区有助于加快速度。

其他信息

http://en.wikipedia.org/wiki/Disk_read-and-write_head

http://en.wikipedia.org/wiki/Hard_disk#Components

stdio函数将填充一个读取缓冲区,其大小为“BUFSIZ”,如stdio.h中所定义,并且每次缓冲区耗尽时,只会进行一次read(2)系统调用。 它们不会为每个消耗的字节执行单独的read(2)系统调用 – 它们会读取大块。 BUFSIZ通常是1024或4096。

如果您愿意,您还可以调整缓冲区的大小来增加它 – 请参阅大多数系统上setbuf / setvbuf / setbuffer的手册页 – 尽管这不太可能对性能产生巨大影响。

另一方面,正如您所注意到的,您可以通过在调用中设置该大小来进行任意大小的read(2)系统调用,尽管在某些时候您会获得递减的回报。

顺便说一下,如果你这样做的话,你也可以使用open(2)而不是fopen(3)。 在fopen’ing只用于文件描述符的文件中没有什么意义。