【问题标题】:Efficiency of fopen, fclosefopen、fclose的效率
【发布时间】:2014-12-02 18:00:02
【问题描述】:

如果在嵌套的for循环中迭代写入文件,在循环之前打开文件并在循环之后关闭文件,而不是在循环内打开和关闭,效率有什么区别吗?请参阅以下内容:

int main(){
    FILE *file1;
    char filename;
    int i, j, N, M;

    for(i=0; i<N; i++){
        file1=fopen(filename, "a");
        for(j=0; j<M; j++){
            fprintf(file1,"%d %d\n", i, j);
        }
        fclose(file1);
    }
return 1;
}

int main(){
    FILE *file1;
    char filename;
    int i, j, N, M;

    file1=fopen(filename, "a");
    for(i=0; i<N; i++){
        for(j=0; j<M; j++){
            fprintf(file1, "%d %d\n", i, j);
        }
    }
    fclose(file1);
    return 1;
}

【问题讨论】:

  • 为什么不对其进行基准测试并自己找出答案?
  • 为什么有人会使用第一个代码 sn-p ?
  • 嗯,在第一种情况下,您正在做更多的工作,因此需要更长的时间。无论它是否实时产生影响,您都只能使用基准测试。
  • 保持文件打开通常更有效,因为当您关闭文件时缓冲区和缓存会被刷新。但是,如果您担心在发生崩溃或电源故障时丢失数据,那么您可能需要采取更具防御性的方法,即反复打开和关闭文件。
  • @PaulR 你不会让文件系统或操作系统更好地处理电源故障吗?我也想过这个问题,但是如果fprintf 发生崩溃,文件仍然是打开的。并且使用缓存和所有功能,即使文件已关闭,也可能根本不会将任何内容写入磁盘。

标签: c performance for-loop io fopen


【解决方案1】:

我做了一个快速基准测试,看看是否存在显着差异。该代码与您的代码略有不同,但仍显示效率差异。另外,我也懒得去考虑缓存等等...

你可以自己看看它是否重要。

测试程序:

#include <stdio.h>
#include <stdlib.h>


#ifdef TEST1
void test(char *filename, int n) {
    int i;
    FILE *fp;

    for (i=0; i<n; i++) {
        fp = fopen(filename, "a");
        if (fp) {
            fprintf(fp, "%d\n", i);
            fclose(fp);
        }
    }
}
#else
void test(char *filename, int n) {
    int i;
    FILE *fp;

    fp = fopen(filename, "a");
    if (!fp)
        return;

    for (i=0; i<n; i++) {
        fprintf(fp, "%d\n", i);
    }

    fclose(fp);
}
#endif

int main(int argc, char *argv[]) {
    char *filename;
    int n;

    if (argc < 3)
        return -1;

    filename = argv[1];
    n = atoi(argv[2]);

    test(filename, n);

    return 0;
}

编译标志和基准测试命令:

gcc -DTEST1 -Wall -O3 -o test1 test.c
gcc -DTEST2 -Wall -O3 -o test2 test.c

time ./test1 test.bin n; rm test.bin # where n is the number of runs
time ./test2 test.bin n; rm test.bin # where n is the number of runs

机器是 2.2GHz Core i7,运行 OS X 的 8GB RAM。

结果:

   n   |  test1  |  test2
-------+---------+---------
10     | 0.009s  | 0.006s
100    | 0.036s  | 0.006s
1000   | 0.340s  | 0.007s
10000  | 2.535s  | 0.011s
100000 | 24.509s | 0.041s

总之,有区别吗?是的。

有显着差异吗?是的,但仅适用于大量(ish)迭代。

这有关系吗?这取决于。您计划进行多少次迭代?直到大约 1000 次迭代,用户不太可能注意到差异。任何更高的东西,您都会开始看到两个不同实现之间的运行时间存在一些显着差异。

归根结底,如果您无需付出太多努力就能提高效率,为什么还要刻意使用效率较低的算法?

【讨论】:

    【解决方案2】:

    如果您关心性能,您可能不想进行额外的打开和关闭操作。但是,如果您非常关心避免数据丢失,那么您可能想要在每次迭代中打开以进行追加、写入和关闭。例如,如果您要添加一个调试打印模块,您可能不太关心性能,而是保留每一位输出。

    例子:

    void debug_print(char *debug_string)
    {
      struct timespec *now;
      int success = clock_gettime(CLOCK_REALTIME, &now);
      /* We ignore errors in the file I/O, because if we can't write to the debug file we have no place to report the problem. */
      FILE *f = fopen("debug.text", "a");
      if (success) {
        fwrite(f, "time %d.%9d: %s\n", now.tv_sec, now.tv_nsec, debug_string);
      } else {
        fwrite(f, "time errno=%d: %s\n", errno, debug_string);
      }
      fclose(f);
    }
    

    【讨论】:

      【解决方案3】:

      是的,有区别。 fopen() 和 fclose() 映射到 open() 和 close() 系统调用,这需要中断来触发上下文切换以运行内核代码。

      【讨论】:

      • -1。如何触发系统调用完全取决于架构。使用中断是可用于启动系统调用的众多方法之一。
      • 是的。我含蓄地指的是 Linux x86。我的错。
      • 在操作系统设计术语中,任何此类内核条目都称为“中断”,而不仅仅是使用相同机制的硬件中断和软件中断。恕我直言,“中断”有点过载。 Java 等又增加了一个重载,这个词现在引起了很多混乱;(
      • 中断是指 x86 上触发同步中断的 int 汇编指令。
      猜你喜欢
      • 1970-01-01
      • 2015-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-05
      • 1970-01-01
      相关资源
      最近更新 更多