所以(只是为了好玩),我只是想写一个C代码来复制文件。我四处阅读,似乎从流调用fgetc()
读取所有函数(我希望这是真的吗?),所以我使用了这个函数:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#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笔记本电脑上的this文件提供了140毫秒的运行时间。
但是,当我尝试使用fread
/ fwrite
时,我会减少运行时间,因为我不断增加为每个调用传输的字节数(即,以下代码中的变量st
),直到它在10ms左右达到峰值!这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#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
答案 0 :(得分:15)
fread()
没有调用fgetc()
来读取每个字节。
它的行为好像重复调用fgetc()
,但它可以直接访问fgetc()
读取的缓冲区,因此它可以直接复制更大量的数据。 / p>
答案 1 :(得分:6)
您忘记了文件缓冲(inode, dentry and page caches)。
在跑步前清除它们:
echo 3 > /proc/sys/vm/drop_caches
基准测试是一门艺术。有关正确的文件系统基准测试,请参阅bonnie++
,iozone
和phoronix
。作为一个特征,bonnie++
将不允许基准的写入量小于可用系统内存的2倍。
为什么呢?
(回答:缓冲效果!)
答案 2 :(得分:3)
stdio函数将填充一个读取缓冲区,其大小为“BUFSIZ”,如stdio.h中所定义,并且每次缓冲区耗尽时只会进行一次read(2)系统调用。他们不会为每个消耗的字节执行单独的read(2)系统调用 - 它们会读取大块。 BUFSIZ通常是1024或4096.
如果您愿意,您还可以调整缓冲区的大小以增加它 - 请参阅大多数系统上setbuf / setvbuf / setbuffer的手册页 - 尽管这不太可能对性能产生巨大影响。
另一方面,正如您所注意到的,您可以通过在调用中设置该大小来进行任意大小的read(2)系统调用,尽管在某些时候您会获得递减的回报。
顺便说一下,如果你这样做,你也可以使用open(2)而不是fopen(3)。在打开一个只用于文件描述符的文件时,没有什么意义。答案 3 :(得分:2)
就像sehe所说的部分原因是缓冲,但还有更多内容,我会解释为什么会这样,同样为什么fgetc()
会给出更多的延迟。
fgetc()
。
fread()
。
因此对于10MiB文件:
fgetc()
被称为:10 485 760次
当fread
具有1KiB缓冲区时,该函数称为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
,这也可以加速程序,因为程序从操作系统内存而不是直接从硬盘读取。这就是sehe的回应所指的。
根据您的机器配置/负载/操作系统/等,您的阅读和写作结果可能会有很大差异,因此他建议清空磁盘缓存以获得更有意义的结果。
当源文件和目标文件在不同的硬盘上时,事情要快得多。对于SDD,我不确定读/写是否完全相互排斥。
总结:对函数的每次调用都有一定的开销,从HDD读取有其他开销,缓存/缓冲有助于加快速度。
其他信息