2

我将两个大(每个近 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 也有同样的疑问。也许我应该使用其他一些功能?

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

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

4

1 回答 1

2

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

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

如果您使用换行符作为数字之间的分隔符,您可以使用fgetsandfputs来读取/写入,从而消除了fscanfand中的格式化成本fprintf。这消除了字符串和数字之间的所有转换。由返回的字符串末尾的换行符在fgets所有数字中都是恒定的,并且不会影响字典比较。

我生成了两个由换行符分隔的 950 万个随机无符号数文件,并运行了时间比较(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;
}
于 2013-04-07T15:12:27.150 回答