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

最佳答案

我会尝试在流上设置一个写缓冲区,并尝试不同大小的缓冲区(例如 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 缓冲区,上述代码(使用从随机整数数组读取图形的模拟进行修改)比 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 是 完全没有值(value) 因为系统已经为你做了。这反过来意味着 Kiwi 的答案是最正确的 ,因为 - 正如安德鲁指出的那样 - 避免了 printf 的检查和转换。

  • 此外,整体增加(谷歌阿姆达尔定律)取决于节省处理时间的比例。显然,如果一小时的精心制作需要一秒钟的节省,那么节省速度加倍,您就可以节省半秒钟;同时将细化速度提高 1% 可为您节省 36 秒,或 72 倍。

    我自己的示例代码被设计为完全面向保存的非常大的图;在这种情况下,写入速度的任何微小改进都可能获得巨大的返回,这在现实世界中可能是不现实的。

    另外(在回答评论时),虽然使用足够小的缓冲区会减慢保存速度,但完全不确定使用更大的缓冲区是否会受益。假设整个图总共生成 1.2Kb 的输出;那么当然任何超过 1.2Kb 的缓冲区值都不会产生任何改进。实际上,分配更多内存可能会对性能产生负面影响。

    关于c - c - 如何在c中比fprintf更快地写入文本文件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47952423/

    10-11 23:17
    查看更多