我有一个1700万个元素的struct
dynamic分配数组。 把它保存到磁盘,我写
fwrite(StructList, sizeof(Struct), NumStructs, FilePointer)
在后面的步骤中,我使用等价的fread
语句读取它,即使用sizeof(Struct)
和NumStructs
的数量。 我期望得到的文件将约3.5 GB(这是所有x64)。
是否有可能传递sizeof(Struct) * NumStructs
作为大小和1
作为计数来加速? 我为什么写操作可能需要几分钟的时间在32 GB的RAM(大量写caching)的快速计算机上摸不着头脑。 我已经运行了自制基准testing,并且caching足够积极,以前的800 MB到1 GB的典型值为400 MB /秒。 PerfMon显示,在fwrite期间它正在占用一个内核的100%。
我在这里看到了这个问题,所以我要问的是,fwrite中是否有一些循环可以通过告诉它写入1个大小为n * s的元素而不是n个大小为s的元素来“加速”。
编辑
我在释放模式下跑了两次,两次都放弃了等待。 然后我以debugging模式运行它,知道fwrite
操作通常会花费更长的时间。 要写入的数据的确切大小是4,368,892,928字节。 在这三种情况下,PerfMon都会显示两次间隔30秒的磁盘写入活动,之后CPU将达到100%的一个内核。 该文件是在这一点73,924,608字节。 我在fwrite
任何一方都有断点,所以我知道它就在那里。 看起来似乎有些东西卡住了,但是我会把它放在一夜之间去看看。
编辑
离开这一夜,它肯定挂在fwrite
,文件永远不会超过70 MB。
这肯定是fwrite
一个问题(我试过VS2012和2010)。
从一个标准的C ++项目开始,我只改变了在一个静态链接中使用多字节字符集x64 target和标准库的多线程调试版本的设置。
下面的代码成功(没有错误检查简洁):
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> int main() { FILE *fp; long long n; unsigned char *data; n = 4LL * 1024 * 1024 * 1024 - 1; data = (unsigned char *)malloc(n * sizeof(unsigned char)); fp = fopen("T:\\test.bin", "wb"); fwrite(data, sizeof(unsigned char), n, fp); fclose(fp); }
在我的机器上的调试版本中,程序在大约1分钟内完成(malloc只需要几秒钟,所以这大部分是fwrite
),平均消耗30%的CPU。 PerfMon显示写入完全发生在最后是一个4 GB(写入缓存)的“闪存”。
将- 1
更改为+ 1
,并重现问题:即时100%的CPU使用率,而且没有任何事情可以写入。 几分钟后,文件的大小仍然是0字节(在我的实际代码中回忆它设法转储70 MB左右)。
这在fwrite
肯定是一个问题,因为下面的代码可以很好地写入文件:
int main() { FILE *fp; long long n; long long counter = 0; long long chunk; unsigned char *data; n = 4LL * 1024 * 1024 * 1024 + 1; data = (unsigned char *)malloc(n * sizeof(unsigned char)); fp = fopen("T:\\test.bin", "wb"); while (counter < n) { chunk = min(n - counter, 100*1000); fwrite(data+counter, sizeof(unsigned char), chunk, fp); counter += chunk; } fclose(fp); }
在我的机器上,这花了45秒,而不是1分钟。 CPU使用率不是恒定的,会突然发生,报告的IO写入比“单块”方法更分散。
如果速度增加是错误的(即由于缓存),我会感到惊讶,因为在编写包含所有相同数据的文件之前,我已经完成了测试,包含随机数据的文件和报告的写入速度(使用缓存)是一样。 所以我敢打赌,至少这个fwrite
实现不像一次传给它的大块。
我还测试了fread
在4 GB + 1的情况下关闭写入文件后立即读取,并及时返回 – 最多几秒(这里没有真正的数据,所以我没有检查它)。
编辑
我用chunk-write方法和4 GB-1文件的单个fwrite调用(两种方法都可以执行的最大尺寸)运行了一些测试。 多次运行程序(使用代码打开文件,使用多个fwrite调用写入,关闭,然后再次打开,单次调用,关闭),毫无疑问,块写入方法返回的速度更快。 在最坏的情况下,单次呼叫所花费的时间为68%,最多只有20%。
这不是fwrite
一个问题 ,但意图(尽管承认不冷酷 )的行为:
fwrite()
函数将从由ptr
指向的数组开始,将大小由size
指定的nitems
元素写入到stream指向的流中。 对于每个对象,应该调用fputc()
函数的size
调用,从数组中取值(按顺序)
所以基本上,通过正确使用fwrite
而不会作弊 ,您正在向fputc
请求数十亿次调用。
考虑到上述要求,很明显,为了使其正常工作,您如何作弊。