如何在c中写入比fprintf更快的文本文件?

问题描述 投票:2回答:3

我必须将一些图形数据(结构数组)保存到文本文件中。我使用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);
    }
}
c performance file text text-files
3个回答
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缓存,因此它可能受益于更大的完全缓冲的写缓存。

当然,这假设您正在写一个相对较慢的媒体,并且节省的时间是相关的;否则,无论什么减慢你的速度都不在这里,因此增加保存性能并不会对你有所帮助。

有一些工具,如profgprof,可以帮助您确定您的计划花费最多的时间。

一个更尴尬的可能性是将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);
}

UPDATE

通过将my_putchar函数声明为inline并使用4K缓冲区,上面的代码(使用随机整数数组的图形模拟修改)比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)

其中约2倍似乎来自缓冲。 Andrew Henle让我注意到我的代码中出现错误:我将结果与无缓冲输出的基线进行比较,但fopen默认使用BUFSIZ值,而我的系统BUFSIZ是8192.所以基本上我已经“发现”了:

  • 8K缓冲区没有优势,4K足够了
  • 我最初使用_IOFBF的建议完全没有价值,因为系统已经为你做了。这反过来意味着新西兰的答案是最正确的,因为 - 正如安德鲁指出的那样 - 避免了printf的检查和转换。

此外,总体增长(谷歌Amdahl定律)取决于节省的处理时间的百分比。显然,如果一小时的精心设计需要一秒钟的节省,那么节省的速度加倍可以节省半秒钟;同时将精化速度提高1%可以节省36秒或72倍。

我自己的示例代码被设计为完全以面向对象的方式保存;在这种情况下,写作速度的任何微小改进都会带来潜在的巨大回报,这在现实世界的情况下可能是不现实的。

另外(在回答评论时),虽然使用足够小的缓冲区会减慢保存速度,但使用更大的缓冲区并不是很有用。假设整个图形整体产生1.2Kb的输出;那么当然任何高于1.2Kb的缓冲值都不会产生任何改进。实际上,分配更多内存可能会对性能产生负面影响。


2
投票

我会写一个小函数说print_graph(int int int)并直接在其中调用write

或类似的东西,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);
}

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);
        } 
    }
© www.soinside.com 2019 - 2024. All rights reserved.