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

时间:2017-12-23 12:16:23

标签: c performance file text text-files

我必须将一些图形数据(结构数组)保存到文本文件中。我使用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);
    }
}

3 个答案:

答案 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缓存,因此可能从更大的完全缓冲的写缓存中受益。

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

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);
}

<强>更新

通过将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.所以我基本上“发现”了那个:

  • 8K缓冲区没有优势,4K足够
  • 我最初使用_IOFBF的建议是完全没价值,因为系统已经为你做了。这反过来意味着 Kiwi的答案是最正确的,因为 - 正如安德鲁指出的那样 - 避免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);
        } 
    }