C fprintf / fscanf优化大文件的速度

时间:2013-04-07 13:49:20

标签: c optimization scanf printf

我将两个大文件(每个大约8 GB)合并为一个文件。我尝试优化它,尽可能好。

void merge() {

   char *array[17]= {"q.out","b.out"}; // names of input files
   FILE *finpt1 = fopen(array[0],"r"), *finpt2 = fopen (array[1],"r"), 
                                     *foutp = fopen("final_.out","w");

   u_int32_t a,b;

   fscanf(finpt1, "%u", &a);
   fscanf(finpt2, "%u", &b);
   int EOF1_my = 0, EOF2_my = 0;

   while (true) {
     if ( a>b ) {
         fprintf( foutp,"%u\n", b);
         if ( fscanf(finpt2, "%u", &b)  == EOF) { EOF2_my = EOF; break; }
      } else  {
        fprintf( foutp,"%u\n", a);
        if ( fscanf(finpt1, "%u", &a) == EOF) { EOF1_my = EOF; break; }
       }
   }

    if ( EOF1_my == EOF) {
      while ( fscanf(finpt2, "%u", &a) != EOF)
         fprintf(foutp, "%u\n", a);
     } else if ( EOF2_my == EOF) {
         while (fscanf(finpt1, "%u", &b) != EOF)
          fprintf( foutp,"%u\n", b);
    }

   fclose(finpt1); fclose(finpt2); fclose(foutp);
}

我怀疑多次调用printf会占用大量资源(我注意到我的日志记录程序的运行速度比通常情况下慢得多)。我认为大多数时候它花费格式化字符串(不写入文件,因为使用了缓冲)。

所以我想知道在自己的内存和写入中形成字符串输出是否更好,例如10000个符号到一个文件,一个吸引fprintf函数 - 像fprintf(“%s”,字符串);?

我对fscanf有同样的疑虑。也许我应该使用其他一些功能?

欢迎任何想法。 提前谢谢!

修复BUG
感谢sfstewman(在评论中注意到问题)。 很酷,这是非常有价值的信息,在我不开始编写测试之前我不会注意到(或者可能永远不会)
谢谢你的代码,但无论如何给我准备好代码,你离开我没有乐趣。
这是我的蛋糕!
想法更有价值,现在我知道词典比较是什么)

1 个答案:

答案 0 :(得分:2)

您的输入都是无符号数字。这意味着您可以使用字典比较而不是数字比较。

要使字典比较适用于无符号数字的字符串,首先要比较字符串的长度(较短的字符串是较小的数字)。如果长度相等,strcmp将指示哪个字符串具有较小的数字。

如果您使用换行符作为数字之间的分隔符,则可以使用fgetsfputs进行读/写,从而消除了fscanffprintf中格式设置的成本。这消除了字符串之间的所有转换数字。 fgets返回的字符串末尾的换行符在所有数字中都是不变的,不会影响字典比较。

我生成了两个由换行符分隔的随机无符号数字的9.5M文件,并运行时序比较(merge1是您的上面的代码,merge2在下面):

% time ./merge1
./merge1  0.89s user 0.08s system 99% cpu 0.974 total
% time ./merge2
./merge2  0.18s user 0.08s system 98% cpu 0.264 total

并且,在更大的测试集上(两个537M的随机数文件,介于0和2 ^ 30-1之间):

% time ./merge1
./merge1  51.22s user 4.57s system 54% cpu 1:41.68 total
% time ./merge2
./merge2  11.13s user 4.68s system 18% cpu 1:26.81 total

这表明数字转换占用了你75%-80%的时间。如果这还不够快,你可以通过自己的缓冲和使用strchr搜索分隔符,并可能使用内存映射文件来进一步优化它。

#include <stdio.h>
#include <string.h>

void merge()
{
   char *array[17]= {"q.out","b.out"}; // names of input files
   FILE *finpt1 = fopen(array[0],"r"), *finpt2 = fopen (array[1],"r"), 
                                     *foutp = fopen("final_.out","w");

   char buf1[32];
   char buf2[32];

   memset(buf1,0,sizeof(buf1));
   memset(buf2,0,sizeof(buf2));

   int EOF1_my = (fgets(buf1, sizeof(buf1), finpt1) == NULL);
   int EOF2_my = (fgets(buf2, sizeof(buf2), finpt2) == NULL);

   size_t l1 = strlen(buf1);
   size_t l2 = strlen(buf2);

   if (!EOF1_my && !EOF2_my)
   {
     for(;;)
     {
       /* unsigned numbers, so use a lexographic comparison */
       int diff = (l1 == l2) ? strcmp(buf1,buf2) : l1-l2;

       if (diff < 0)
       {
         fputs( buf1, foutp );
         memset(buf1,0,sizeof(buf1));
         EOF1_my = (fgets(buf1, sizeof(buf1), finpt1) == NULL);
         if (EOF1_my) break;
         l1 = strlen(buf1);
       }
       else
       {
         fputs( buf2, foutp );
         memset(buf2,0,sizeof(buf2));
         EOF2_my = (fgets(buf2, sizeof(buf2), finpt2) == NULL);
         if (EOF2_my) break;
         l2 = strlen(buf2);
       }
     }
   }

   FILE* frest = NULL;
   if (!EOF1_my || !EOF2_my)
   {
     if (!EOF1_my)
     {
       fputs(buf1, foutp);
       frest = finpt1;
     }
     else
     {
       fputs(buf2, foutp);
       frest = finpt2;
     }

     memset(buf1,0,sizeof(buf1));

     while(fgets(buf1,sizeof(buf1),frest) != NULL)
     {
       fputs(buf1,foutp);
     }
   }

   fclose(finpt1); fclose(finpt2); fclose(foutp);
}

int main()
{
  merge();
  return 0;
}