fputwc和fgetwc是否明显快于fprintf和fscanf?为什么会这样?

时间:2015-06-07 19:58:37

标签: c performance io wchar-t

我正在处理使用wcahr_t类型字符的文件。我想让我的程序更快。

fprintf替换为fputwcfscanf并尽可能用fgetwc代替是否有意义?

如果是这样,为什么?

1 个答案:

答案 0 :(得分:2)

我每次调用仅使用一个wchar_t进行了一些快速分析,确实存在明显的差异。首先,代码:

#include "stdio.h"
#include "time.h"
#include "wchar.h"

wchar_t text[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$";
const int N = 64;
const int M = 500000;
int main()
{
    int i, j;
    FILE *f1 = fopen("out1", "w");
    FILE *f2 = fopen("out2", "w");

    if (f1 == NULL || f2 == NULL)
    {
        printf("Failed to create file.\n");
        return -1;
    }

    clock_t time1 = clock();
    for (i = 0; i < M; i++)
        for (j = 0; j < N; j++)
            fprintf(f1, "%c", text[j]);
    time1 = clock() - time1;


    clock_t time2 = clock();
    for (i = 0; i < M; i++)
        for (j = 0; j < N; j++)
            fputwc(text[j], f2);
    time2 = clock() - time2;

    printf("fprintf: %d ticks\nfputwc: %d ticks\n", time1, time2);
    return 0;
}

gcc的输出:

  

fprintf:5663 ticks

     

fputwc:3307 ticks

clang的输出:

  

fprintf:4696 ticks

     

fputwc:3338 ticks

写入stdout或内存缓冲区具有相同的时差,因此这与它们如何写入或何时刷新等无关。它只是函数实现。我们来看看它们:

最明显的区别是fprintf采用了必须解析的格式。因此,它必须做的最小工作是:

while (*fmt) {
    if (*fmt++ == '%') {
        switch (*fmt)
        {
        case 'c': *output = va_arg(args, int); break;
        ...
      }
    }
}

正如您所看到的,代码必须执行fputwc不进行的多次检查和增量,这意味着额外的执行时间。 case 'c'在其他情况下的位置实际上很重要,它越低,它就会越慢。(见评论)这些差异可能会占用大部分额外时间。

特别是在printf("%c", x)的情况下,我们有:

  • 设置初始状态 - 让我们说5-30条说明
  • 阅读第一个字符并与'%'进行比较 - 约5条说明
  • 读取第二个字符,切换语句 - 大约10条指令。

在此之后,它与fputwc几乎完全相同。每次通话所有这些额外的指令都会加起来。

还有一些其他因素可以影响这一点,例如传递额外的参数,但是一个好的编译器可以优化它。

所有这一切,这是一种非常低效的处理方式。您可能应该写入缓冲区,然后调用fwrite(我的时钟与fputwc类似)并在一次调用中转储整个缓冲区。需要注意的一点是,fwrite(f, sizeof(wchar_t), N, text);实际上也会输出0个字节,最后是"a\0b\0c\0",依此类推。