wut*_*wut 4 c performance text file text-files
我必须将一些图形数据(结构数组)保存到文本文件中。我使用 fprintf 制作了工作程序,但为了额外的分数,我需要更快。我花了几个小时谷歌搜索是否有更快的东西并尝试使用 fwrite (但我无法将 fwrite 作为文本)我真的找不到任何其他功能等。
这是我使用 fprintf 的写入函数:
void save_txt(const graph_t * const graph, const char *fname)
{
int count = graph->num_edges, i = 0;
FILE *f = fopen(fname, "w");
while (count > 0) {
int r = fprintf(f, "%d %d %d\n", (graph->edges[i].from), (graph->edges[i].to), (graph->edges[i].cost));
i++;
if (r >= 6) {
count -= 1;
} else {
break;
}
}
if (f) {
fclose(f);
}
}
Run Code Online (Sandbox Code Playgroud)
我会尝试在流上设置写入缓冲区,并尝试不同大小的缓冲区(例如 1K、2K、4K、8K 等)。请注意,默认情况下您的文件已经使用 BUFSIZ 值的缓冲区,并且可能已经足够了。
#define BUFFERSIZE 0x1000
void save_txt(const graph_t * const graph, const char *fname)
{
int count = graph->num_edges, i = 0;
unsigned char buf[BUFFERSIZE];
FILE *f = fopen(fname, "w");
setvbuf(f, buf, _IOFBF, BUFFERSIZE);
...
Run Code Online (Sandbox Code Playgroud)
输出文件f天生带有默认的 BUFSIZ 缓存,因此它可能会受益于更大的全缓冲写入缓存。
当然,这假设您正在写入相对较慢的介质,并且节省的时间是相关的;否则,任何拖慢你速度的东西都不在这里,因此提高保存性能不会对你有明显帮助。
有类似prof和gprof之类的工具可以帮助您确定程序在哪里花费了最多的时间。
一种更尴尬的可能性是将 Kiwi 的答案与缓冲写入调用合并,以避免 printf 中验证要使用哪种格式的代码,因为您已经知道这一点,并使用尽可能少的 I/O 调用(即使只是一个如果 BUFFERSIZE 大于目标文件的长度)。
// These variables must now be global, declared outside save_txt.
char kiwiBuf[BUFFERSIZE];
size_t kiwiPtr = 0;
FILE *f;
void my_putchar(char c) {
kiwiBuf[kiwiPtr++] = c;
// Is the buffer full?
if (kiwiPtr == BUFFERSIZE) {
// Yes, empty the buffer into the file.
flushBuffer();
}
}
void flushBuffer() {
if (kiwiPtr) {
fwrite(kiwiBuf, kiwiPtr, 1, f);
kiwiPtr = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
您现在需要在关闭之前刷新缓冲区:
void save_txt(const graph_t * const graph, const char *fname)
{
int i, count = graph->num_edges;
f = fopen(fname, "w");
if (NULL == f) {
fprintf(stderr, "Error opening %s\n", fname);
exit(-1);
}
for (i = 0; i < count; i++) {
my_put_nbr(graph->edges[i].from);
my_putchar(' ');
my_put_nbr(graph->edges[i].to);
my_putchar(' ');
my_put_nbr(graph->edges[i].cost);
my_putchar('\n');
}
flushBuffer();
fclose(f);
}
Run Code Online (Sandbox Code Playgroud)
更新
通过将my_putchar函数声明为inline4K 缓冲区,上面的代码(使用从随机整数数组读取图形的模拟进行修改)比fprintf上面的代码快大约 6 倍
Linux mintaka 4.12.8-1-default #1 SMP PREEMPT Thu Aug 17 05:30:12 UTC 2017 (4d7933a) x86_64 x86_64 x86_64 GNU/Linux
gcc version 7.1.1 20170629 [gcc-7-branch revision 249772] (SUSE Linux)
Run Code Online (Sandbox Code Playgroud)
其中大约 2 倍似乎来自缓冲。Andrew Henle 让我注意到代码中的一个错误:我将结果与无缓冲输出的基线进行比较,但fopen默认使用 BUFSIZ 值,在我的系统上 BUFSIZ 是 8192。所以基本上我“发现”了这一点:
printf检查和转换。此外,总体增长(谷歌阿姆达尔定律)取决于用于保存的处理时间的比例。显然,如果一小时的阐述需要一秒钟的保存,那么将保存速度加倍可以节省半秒;而将精细化速度提高 1% 可以节省 36 秒,即 72 倍。
我自己的示例代码被设计为完全面向保存,具有非常大的图形;在这种情况下,写入速度的任何微小改进都可能带来巨大的回报,这在现实世界中可能是不现实的。
另外(回答评论),虽然使用足够小的缓冲区会减慢保存速度,但完全不确定使用较大的缓冲区是否会受益。假设整个图总共生成 1.2Kb 的输出;那么当然任何高于 1.2Kb 的缓冲区值都不会产生任何改进。实际上,分配更多内存可能会对性能产生负面影响。