我必须将一些图形数据(结构数组)保存到文本文件中。我使用fprintf制作了工作程序但是为了加分,我需要更快。我有几个小时的谷歌搜索,如果有更快的东西,并尝试使用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);
}
}
答案 0 :(得分:4)
我会尝试在流上设置写缓冲区,并尝试使用不同大小的缓冲区(例如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);
...
输出文件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;
}
}
现在需要在关闭前刷新缓冲区:
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);
}
<强>更新强>
通过将my_putchar
函数声明为inline
并使用4K缓冲区,上面的代码(使用从随机整数数组中读取的图形模拟进行修改)比{{1>快约6倍
fprintf
其中约2倍似乎来自缓冲。 Andrew Henle让我注意到我的代码中出现错误:我将结果与 unbuffered 输出的基线进行比较,但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)
默认使用BUFSIZ值而 my 系统BUFSIZ是8192.所以我基本上“发现”了那个:
fopen
的检查和转换。此外,总体增长(谷歌Amdahl定律)取决于节省的处理时间的百分比。显然,如果一小时的精心设计需要一秒钟的节省,那么节省的速度加倍可以节省半秒钟;将精化速度提高1%可以节省36秒或72倍。
我自己的示例代码被设计为使用非常大的图形完全保存导向;在这种情况下,写作速度的任何微小改进都会获得巨大的回报,这在现实世界案例中可能是不现实的。
另外(在回答评论时),虽然使用足够小的缓冲区会减慢保存速度,但使用更大的缓冲区并不是很有用。假设整个图形整体产生1.2Kb的输出;那么当然 1.2Kb以上的任何缓冲区值都不会产生任何改进。实际上,分配更多内存可能会对性能产生负面影响。
答案 1 :(得分:2)
我会写一个小函数说print_graph(int int int) 并直接在其中调用
或类似的东西,my_putchar是一个写调用
int my_put_nbr(int nb)
{
if (nb < 0)
{
my_putchar('-');
nb = -nb;
}
if (nb <= 9)
my_putchar(nb + 48);
else
{
my_put_nbr(nb / 10);
my_put_nbr(nb % 10);
}
return (0);
}
答案 2 :(得分:1)
我必须比fprintf快1.3倍,这里的代码对我有用。我不得不说我必须多次提交它,有时候我通过了相同代码的5次测试中的1次。总之,它比fprintf快,但不能快1.3倍......
void save_txt(const graph_t * const graph, const char *fname)
{
int count = graph->num_edges, i = 0;
char c = '\n';
char d = ' ';
char buffer[15];
FILE *f = fopen(fname, "w");
while (count > 0) {
itoa(graph->edges[i].from,buffer,10);
fputs(buffer, f);
putc(d, f);
itoa(graph->edges[i].to,buffer,10);
fputs(buffer, f);
putc(d, f);
itoa(graph->edges[i].cost,buffer,10);
fputs(buffer, f);
putc(c, f);
i++;
count -= 1;
}
if (f) {
fclose(f);
}
}