如何有效地用 C 编写文本文件,文件大小为 5GB
how to effectively write text file in C and the size of file would be 5GB
FILE *fp;
fp = fopen(pch, "wb");
while(1)
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
我用这些代码写入txt文件,但是当输出文件很大时(它的大小可能会增长到5GB)它会很慢,我需要等待很长时间。
谁能告诉我写入 txt 文件的更好方法?
编辑:
p
是一个 const char *
变量。我可能需要在计算机上等待一个小时。我只是检查 txt 文件的大小是否增长到 20GB.
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
我已经测试了您代码的空实现以查看 fwrite
性能,我相信瓶颈 绝对不是 fwrite
。
#include <stdio.h>
#include <string.h>
char *icmDisassemble(int x, int y) {
return "The rain in Spain falls mostly on the plain";
// return "D'oh";
// return "Quel ramo del lago di Como, che volge a mezzogiorno, tra due catene non interrotte di monti, tutto a seni e a golfi, a seconda dello sporgere e del rientrare di quelli, vien, quasi a un tratto, a ristringersi, e a prender corso e figura di fiume, tra un promontorio a destra, e un’ampia costiera dall’altra parte; e il ponte, che ivi congiunge le due rive, par che renda ancor più sensibile all’occhio questa trasformazione, e segni il punto in cui il lago cessa, e l’Adda rincomincia, per ripigliar poi nome di lago dove le rive, allontanandosi di nuovo, lascian l’acqua distendersi e rallentarsi in nuovi golfi e in nuovi seni.";
}
#define ICM_SR_SCHED 0
int icmSimulate(int x, int y) {
return ICM_SR_SCHED;
}
int main() {
int done;
int i = 0;
int processor[1] = { 0 };
int currentPC = 0;
FILE *fp;
fp = fopen("test.dat", "w");
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
}
测试结果
touch test.dat; ( ./testx & ); for i in $( seq 1 7 ); do \
date | tr "\n" "\t"; du -sh test.dat; \
sleep 10; done; \
killall -KILL testx; rm -f test.dat
写入速度在 50 到 60 MB/s 之间,或者在桌面 SATA 磁盘(不是 SSD)上为每分钟 2 Gb。这比 dd
:
慢大约 50%,或相同的数量级
time dd if=/dev/zero of=test.dat bs=1M count=5300
5300+0 records in
5300+0 records out
5557452800 bytes (5.6 GB) copied, 61.5375 s, 90.3 MB/s
real 1m2.105s
user 0m0.000s
sys 0m9.544s
我的硬件时钟保持在 100 MB/s 左右,所以 90.3 MB/s 是一个可信的数字(我 现在正在使用该系统,并且可能会减慢速度它下降了一点)。
改变字符串长度不会显着改变时间:
// "D'oh"
Fri Jun 12 19:36:50 CEST 2015 1.5M test.dat
Fri Jun 12 19:37:00 CEST 2015 751M test.dat
Fri Jun 12 19:37:10 CEST 2015 1.5G test.dat
Fri Jun 12 19:37:20 CEST 2015 2.2G test.dat
Fri Jun 12 19:37:31 CEST 2015 2.9G test.dat
Fri Jun 12 19:37:41 CEST 2015 3.6G test.dat
Fri Jun 12 19:37:51 CEST 2015 4.4G test.dat
// First lengthy sentence of *I Promessi Sposi*
Fri Jun 12 19:39:42 CEST 2015 8.4M test.dat
Fri Jun 12 19:39:52 CEST 2015 1.2G test.dat
Fri Jun 12 19:40:02 CEST 2015 2.1G test.dat
Fri Jun 12 19:40:14 CEST 2015 3.1G test.dat
Fri Jun 12 19:40:25 CEST 2015 4.0G test.dat
Fri Jun 12 19:40:35 CEST 2015 4.8G test.dat
Fri Jun 12 19:40:45 CEST 2015 5.7G test.dat
// "The rain in Spain"
Fri Jun 12 19:41:21 CEST 2015 7.3M test.dat
Fri Jun 12 19:41:31 CEST 2015 1.2G test.dat
Fri Jun 12 19:41:43 CEST 2015 2.1G test.dat
Fri Jun 12 19:41:53 CEST 2015 3.0G test.dat
Fri Jun 12 19:42:03 CEST 2015 3.9G test.dat
Fri Jun 12 19:42:13 CEST 2015 4.6G test.dat
Fri Jun 12 19:42:23 CEST 2015 5.3G test.dat
那么瓶颈在哪里呢?
我真的没几个选择。
在磁盘上。尝试 运行 在您的计算机上运行我的代码几分钟。它应该写入大约 10 GB 的垃圾。明显较低的数字可能表明磁盘设置、文件系统甚至物理支持或接口硬件存在问题。
是icmDisassemble。你说它不是,但让我们假设它经常 returns 零长度字符串 。通过返回“”,我得到更差的性能:1.5 Gb/minute 而不是 4-5.
在后一种情况下,您可以尝试计算得到的行长度:
tr -c "\n" "." < YourLargeOutputFile | sort | uniq -c
这是 strings
在随机文件上的结果,显示大多数行只有四个字节长(如预期):
10931 ....
4319 .....
1680 ......
629 .......
288 ........
142 .........
54 ..........
21 ...........
18 ............
6 .............
3 ..............
4 ...............
3 ................
1 .................
如果您看到大量的零长度行,这可能是一回事:
const char *p = icmDisassemble(processor[i], currentPC);
// Ignore zero-length output.
if (p[0]) {
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
}
另一种可能是尝试使用更大的缓冲区。使用 64K 缓冲区,这应该绰绰有余,我再次获得正常性能,即使写入零长度字符串以及回车 returns:
Fri Jun 12 20:03:15 CEST 2015 6.5M test.dat
Fri Jun 12 20:03:25 CEST 2015 1.3G test.dat
Fri Jun 12 20:03:35 CEST 2015 2.1G test.dat
Fri Jun 12 20:03:45 CEST 2015 3.0G test.dat
Fri Jun 12 20:03:56 CEST 2015 3.7G test.dat
Fri Jun 12 20:04:06 CEST 2015 4.3G test.dat
Fri Jun 12 20:04:17 CEST 2015 5.2G test.dat
这是修改后的代码(注意缓冲区 不是 零终止 - "\n" 覆盖终止零)。
#define ICM_BUF_LEN 0x10000
char *buffer = malloc(ICM_BUF_LEN);
size_t bufptr = 0;
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
if ((strlen(p) + bufptr + 1) >= ICM_BUF_LEN) {
fwrite(buffer, 1, bufptr, fp);
bufptr = 0;
}
strcpy(buffer + bufptr, p);
bufptr += strlen(p);
buffer[bufptr++] = '\n';
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
fwrite(buffer, 1, bufptr, fp);
free(buffer); buffer = NULL;
保存 strlen
调用(将第一个 strlen 保存到变量中并使用 memcpy
)不会明显改变结果。在我的系统上,两倍大的缓冲区也无法带来任何好处。
是icmDisassemble。请注意,不是总是,而是有时。也许在极少数情况下,它会发现一些笨拙的数据并且它会阻塞,或者失去很长时间来恢复或双重检查或调用昂贵的功能。我们如何检查这个?您可以为函数计时——给定缓慢的数量级,我们只需要能够理解毫秒;有几个片段可以做到这一点。
int times[1000];
for (j = 0; j < 1000; j++) { times[j] = 0; }
while (!done)
{
size_t s;
int ms1 = getTimeMilliseconds();
const char *p = icmDisassemble(processor[i], currentPC);
int ms2 = getTimeMilliseconds() - ms1;
if (ms2 > 999) {
ms2 = 999;
}
times[ms2]++;
在 运行 之后,或者如果时钟超过适当的 运行 时间,您将数组转储到标准输出,忽略零条目,并得到如下内容:
times
----
0 182493 <-- times obviously not zero, but still < 1 ms
1 9837
2 28
3 5
6 1
135 1 <---- two suspicious glitches (program preempted by kernel?)
337 1 <--
999 5 <-- on five occasions the function has stalled
如果事实证明是这种情况,您可以在 icmDisassemble 调用后立即添加一个回溯部分,以检查时间并在第一次超过合理限制时转储诊断信息。
同时比较 wall time 和 CPU 时间可以产生有价值的信息 - 例如揭示 其他东西 正在抢占您的程序,或者它花费了大部分时间等什么。
FILE *fp;
fp = fopen(pch, "wb");
while(1)
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
我用这些代码写入txt文件,但是当输出文件很大时(它的大小可能会增长到5GB)它会很慢,我需要等待很长时间。 谁能告诉我写入 txt 文件的更好方法?
编辑:
p
是一个 const char *
变量。我可能需要在计算机上等待一个小时。我只是检查 txt 文件的大小是否增长到 20GB.
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
我已经测试了您代码的空实现以查看 fwrite
性能,我相信瓶颈 绝对不是 fwrite
。
#include <stdio.h>
#include <string.h>
char *icmDisassemble(int x, int y) {
return "The rain in Spain falls mostly on the plain";
// return "D'oh";
// return "Quel ramo del lago di Como, che volge a mezzogiorno, tra due catene non interrotte di monti, tutto a seni e a golfi, a seconda dello sporgere e del rientrare di quelli, vien, quasi a un tratto, a ristringersi, e a prender corso e figura di fiume, tra un promontorio a destra, e un’ampia costiera dall’altra parte; e il ponte, che ivi congiunge le due rive, par che renda ancor più sensibile all’occhio questa trasformazione, e segni il punto in cui il lago cessa, e l’Adda rincomincia, per ripigliar poi nome di lago dove le rive, allontanandosi di nuovo, lascian l’acqua distendersi e rallentarsi in nuovi golfi e in nuovi seni.";
}
#define ICM_SR_SCHED 0
int icmSimulate(int x, int y) {
return ICM_SR_SCHED;
}
int main() {
int done;
int i = 0;
int processor[1] = { 0 };
int currentPC = 0;
FILE *fp;
fp = fopen("test.dat", "w");
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
}
测试结果
touch test.dat; ( ./testx & ); for i in $( seq 1 7 ); do \
date | tr "\n" "\t"; du -sh test.dat; \
sleep 10; done; \
killall -KILL testx; rm -f test.dat
写入速度在 50 到 60 MB/s 之间,或者在桌面 SATA 磁盘(不是 SSD)上为每分钟 2 Gb。这比 dd
:
time dd if=/dev/zero of=test.dat bs=1M count=5300
5300+0 records in
5300+0 records out
5557452800 bytes (5.6 GB) copied, 61.5375 s, 90.3 MB/s
real 1m2.105s
user 0m0.000s
sys 0m9.544s
我的硬件时钟保持在 100 MB/s 左右,所以 90.3 MB/s 是一个可信的数字(我 现在正在使用该系统,并且可能会减慢速度它下降了一点)。
改变字符串长度不会显着改变时间:
// "D'oh"
Fri Jun 12 19:36:50 CEST 2015 1.5M test.dat
Fri Jun 12 19:37:00 CEST 2015 751M test.dat
Fri Jun 12 19:37:10 CEST 2015 1.5G test.dat
Fri Jun 12 19:37:20 CEST 2015 2.2G test.dat
Fri Jun 12 19:37:31 CEST 2015 2.9G test.dat
Fri Jun 12 19:37:41 CEST 2015 3.6G test.dat
Fri Jun 12 19:37:51 CEST 2015 4.4G test.dat
// First lengthy sentence of *I Promessi Sposi*
Fri Jun 12 19:39:42 CEST 2015 8.4M test.dat
Fri Jun 12 19:39:52 CEST 2015 1.2G test.dat
Fri Jun 12 19:40:02 CEST 2015 2.1G test.dat
Fri Jun 12 19:40:14 CEST 2015 3.1G test.dat
Fri Jun 12 19:40:25 CEST 2015 4.0G test.dat
Fri Jun 12 19:40:35 CEST 2015 4.8G test.dat
Fri Jun 12 19:40:45 CEST 2015 5.7G test.dat
// "The rain in Spain"
Fri Jun 12 19:41:21 CEST 2015 7.3M test.dat
Fri Jun 12 19:41:31 CEST 2015 1.2G test.dat
Fri Jun 12 19:41:43 CEST 2015 2.1G test.dat
Fri Jun 12 19:41:53 CEST 2015 3.0G test.dat
Fri Jun 12 19:42:03 CEST 2015 3.9G test.dat
Fri Jun 12 19:42:13 CEST 2015 4.6G test.dat
Fri Jun 12 19:42:23 CEST 2015 5.3G test.dat
那么瓶颈在哪里呢?
我真的没几个选择。
在磁盘上。尝试 运行 在您的计算机上运行我的代码几分钟。它应该写入大约 10 GB 的垃圾。明显较低的数字可能表明磁盘设置、文件系统甚至物理支持或接口硬件存在问题。
是icmDisassemble。你说它不是,但让我们假设它经常 returns 零长度字符串 。通过返回“”,我得到更差的性能:1.5 Gb/minute 而不是 4-5.
在后一种情况下,您可以尝试计算得到的行长度:
tr -c "\n" "." < YourLargeOutputFile | sort | uniq -c
这是 strings
在随机文件上的结果,显示大多数行只有四个字节长(如预期):
10931 ....
4319 .....
1680 ......
629 .......
288 ........
142 .........
54 ..........
21 ...........
18 ............
6 .............
3 ..............
4 ...............
3 ................
1 .................
如果您看到大量的零长度行,这可能是一回事:
const char *p = icmDisassemble(processor[i], currentPC);
// Ignore zero-length output.
if (p[0]) {
fwrite(p, sizeof(char), strlen(p) / sizeof(char), fp);
fwrite("\n", sizeof(char), 1, fp);
}
另一种可能是尝试使用更大的缓冲区。使用 64K 缓冲区,这应该绰绰有余,我再次获得正常性能,即使写入零长度字符串以及回车 returns:
Fri Jun 12 20:03:15 CEST 2015 6.5M test.dat
Fri Jun 12 20:03:25 CEST 2015 1.3G test.dat
Fri Jun 12 20:03:35 CEST 2015 2.1G test.dat
Fri Jun 12 20:03:45 CEST 2015 3.0G test.dat
Fri Jun 12 20:03:56 CEST 2015 3.7G test.dat
Fri Jun 12 20:04:06 CEST 2015 4.3G test.dat
Fri Jun 12 20:04:17 CEST 2015 5.2G test.dat
这是修改后的代码(注意缓冲区 不是 零终止 - "\n" 覆盖终止零)。
#define ICM_BUF_LEN 0x10000
char *buffer = malloc(ICM_BUF_LEN);
size_t bufptr = 0;
while (!done)
{
const char *p = icmDisassemble(processor[i], currentPC);
if ((strlen(p) + bufptr + 1) >= ICM_BUF_LEN) {
fwrite(buffer, 1, bufptr, fp);
bufptr = 0;
}
strcpy(buffer + bufptr, p);
bufptr += strlen(p);
buffer[bufptr++] = '\n';
done = (icmSimulate(processor[i], 1) != ICM_SR_SCHED);
}
fwrite(buffer, 1, bufptr, fp);
free(buffer); buffer = NULL;
保存 strlen
调用(将第一个 strlen 保存到变量中并使用 memcpy
)不会明显改变结果。在我的系统上,两倍大的缓冲区也无法带来任何好处。
是icmDisassemble。请注意,不是总是,而是有时。也许在极少数情况下,它会发现一些笨拙的数据并且它会阻塞,或者失去很长时间来恢复或双重检查或调用昂贵的功能。我们如何检查这个?您可以为函数计时——给定缓慢的数量级,我们只需要能够理解毫秒;有几个片段可以做到这一点。
int times[1000]; for (j = 0; j < 1000; j++) { times[j] = 0; } while (!done) { size_t s; int ms1 = getTimeMilliseconds(); const char *p = icmDisassemble(processor[i], currentPC); int ms2 = getTimeMilliseconds() - ms1; if (ms2 > 999) { ms2 = 999; } times[ms2]++;
在 运行 之后,或者如果时钟超过适当的 运行 时间,您将数组转储到标准输出,忽略零条目,并得到如下内容:
times
----
0 182493 <-- times obviously not zero, but still < 1 ms
1 9837
2 28
3 5
6 1
135 1 <---- two suspicious glitches (program preempted by kernel?)
337 1 <--
999 5 <-- on five occasions the function has stalled
如果事实证明是这种情况,您可以在 icmDisassemble 调用后立即添加一个回溯部分,以检查时间并在第一次超过合理限制时转储诊断信息。
同时比较 wall time 和 CPU 时间可以产生有价值的信息 - 例如揭示 其他东西 正在抢占您的程序,或者它花费了大部分时间等什么。