2012-04-12 10 views
4

入力は、1行にクライアント情報が含まれる約70GBの単一ファイルです。プログラムはこのファイルを読み取り、クライアントごとに1つのファイルを作成します。クライアントは8000人ありますが、40000人のクライアントにプロビジョニングする必要があります。現時点では、UNIXのsortコマンドを使用してファイルをクライアントでソートし、クライアントファイルを書き込みます。このようにして、プログラムはファイルを作成するための単一のファイルハンドラのみをオープンします。 ソートコマンドを使用するのは約1.5時間です。しかし、これは8000のファイルハンドラを開いたままにする必要があることを意味します。カーネルパラメータを変更する必要があるかもしれません。 カーネルパラメータを変更せずに非常に多くのファイルを開くことは可能ですか?私はlibeventのウェブサイトに行きましたが、それが正しい解決策であるかどうかは分かりません。Cで複数のファイルを開く/書き込む

+0

ファイルの順序付き(クライアントごとに)インデックスを維持しておき、クライアントを1つずつ処理できませんか? –

答えて

11

は同時に開いて処理し、また、データをソートする必要があります。あなたがソートされた各クライアントラインを必要としない限り、ソートは無駄です。

おそらく、その行のいくつかの項目でクライアントを識別できます。その後、あなたの擬似コードは次のようになり、それは各行の最初の8つの文字だのは、(例えば)としましょう:

delete all files matching "*_out.dat" 
for each line in file: 
    key = left (line, 8) 
    open file key + "_out.dat" for append 
    write line to file 
    close file 

それだこと。シンプル。一度に1つのファイルしか開いておらず、時間がないので並べ替えが無駄になりました。次の行は、別のキーを持っていない限り、

  1. 前の行のためのファイルを閉じないでください:

    は今、彼らがいる間、それに作ることができるさらに改善があります。これは、同じキーの行に100個のレコードがある状況を捕捉し、その場合にファイルを開いたままにします。

  2. 最近使用されたリスト(たとえば16個の異なるキー)のようなオープンファイルハンドルのキャッシュを維持します。この場合も、ファイルハンドルを再利用する必要がなくなるまでクロージャが終了しませんが、クラスタがより効率的な状況(たとえば、顧客1,2,3,7,1,2,3,2,2,3、 7,4、...)。

しかし、基本的な理論は同じまま:あなたが少ないとによって得ることができたときに、一度に8000(または40000)ファイルを開こうとしないでください。


あるいは、単にデータベースにすべてをスタッシュ、次にクエリ一連の各ファイルを作成するためにクエリを使用して、データを処理します。上記の解決策よりもが速くかどうかをテストする必要があります。実際にはとなります。提案がここに記載されています。 測定、推測しないでください!私はその最適化のマントラを呼び出してきたので、今


、これは私のハードウェアに固有のものであり、あなたに異なる場合があることを念頭に置いて、のは、いくつかのタイミングをやらせます。

各行の最初の8文字が1000000010032767の間の乱数である100万行のファイルを生成する次のスクリプトから始めます。私たちは、私たちの顧客番号を与えるために8を含めて文字5を使用します、顧客ごとにおよそ100行1万のお客様:

#!/bin/bash 
line='the quick brown fox jumps over the lazy dog' 
for p0 in 1 2 3 4 5 6 7 8 9 0 ; do 
for p1 in 1 2 3 4 5 6 7 8 9 0 ; do 
    for p2 in 1 2 3 4 5 6 7 8 9 0 ; do 
    for p3 in 1 2 3 4 5 6 7 8 9 0 ; do 
    for p4 in 1 2 3 4 5 6 7 8 9 0 ; do 
    for p5 in 1 2 3 4 5 6 7 8 9 0 ; do 
     ((x = 10000000 + $RANDOM)) 
     echo "$x$line" 
    done 
    done 
    done 
    done 
done 
done 

生成されたファイルのサイズは約50Mです。 2つのコピーを別のファイルに連結するだけで、100Mまで拡張することができます。これによって、顧客あたり約200本の回線が提供されます。これは、単に1つのファイルにすべてのエントリを作成するためのベースラインの数字を与え、そして実行するために、第2の下にかかる

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

#define FOUT_STR "1234_out.dat" 

int main (void) { 
    FILE *fIn, *fOut; 
    char outFile[sizeof (FOUT_STR)]; 
    char buff[1000]; 

    if ((fIn = fopen ("data.dat", "r")) == NULL) { 
     printf ("Error %d opening 'data.dat'\n", errno); 
     return 1; 
    } 

    memcpy (outFile, FOUT_STR, sizeof (FOUT_STR)); 
    if ((fOut = fopen (outFile, "w")) == NULL) { 
     printf ("Error %d opening '%s'\n", errno, outFile); 
     return 1; 
    } 

    while (fgets (buff, sizeof (buff), fIn) != NULL) { 
     fputs (buff, fOut); 
    } 

    fclose (fOut); 
    fclose (fIn); 
    return 0; 
} 


は今、次のプログラムを調べます。今


のは、新しいファイルごとに200行を開き、1を持ってみましょう - これはファイルがすでに顧客によってソートされた場合は、参照してくださいね動作ですが:

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

#define FOUT_STR "1234_out.dat" 

int main (void) { 
    FILE *fIn, *fOut; 
    char outFile[sizeof (FOUT_STR)]; 
    char buff[1000]; 
    char custNum[5]; 
    int i = -1; 

    if ((fIn = fopen ("data.dat", "r")) == NULL) { 
     printf ("Error %d opening 'data.dat'\n", errno); 
     return 1; 
    } 

    fOut = NULL; 
    while (fgets (buff, sizeof (buff), fIn) != NULL) { 
     i++; 
     if ((i % 200) == 0) { 
      if (fOut != NULL) 
       fclose (fOut); 
      sprintf (custNum, "%04d", i/200); 
      memcpy (outFile, FOUT_STR, sizeof (FOUT_STR)); 
      memcpy (outFile, custNum, 4); 
      if ((fOut = fopen (outFile, "w")) == NULL) { 
       printf ("Error %d opening '%s'\n", errno, outFile); 
       break; 
      } 
     } 
     fputs (buff, fOut); 
    } 
    if (fOut != NULL) 
     fclose (fOut); 

    fclose (fIn); 
    return 0; 
} 

これは、(2S程度かかり0:00:02)を200Mファイルと400Mファイルでテストすると、それが線形に拡大することが示されます。つまり、ソートされた70Gファイルでは、約1400秒か0:23:20かを見ています。これは1.5時間(1:30:00)のソートコストの上にあり、総コストは1:53:20です。


今度は、単純にすべての行のためのアペンドのための各ファイルを開き、単純なプログラムを実装してみましょう:

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

#define FOUT_STR "1234_out.dat" 

int main (void) { 
    FILE *fIn, *fOut; 
    char outFile[sizeof (FOUT_STR)]; 
    char buff[1000]; 

    if ((fIn = fopen ("data.dat", "r")) == NULL) { 
     printf ("Error %d opening 'data.dat'\n", errno); 
     return 1; 
    } 

    while (fgets (buff, sizeof (buff), fIn) != NULL) { 
     memcpy (outFile, FOUT_STR, sizeof (FOUT_STR)); 
     memcpy (outFile, &(buff[4]), 4); 
     if ((fOut = fopen (outFile, "a")) == NULL) { 
      printf ("Error %d opening '%s'\n", errno, outFile); 
      break; 
     } 
     fputs (buff, fOut); 
     fclose (fOut); 
    } 

    fclose (fIn); 
    return 0; 
} 

我々は100Mファイルでこれを実行すると、それは(夜12時04分04秒)244sを取ります。 200Mファイルと400Mファイルを使用したテストは、線形スケーリングを示します。したがって、70Gファイルの場合、それは012:51:30:40:40になります.は実際にはではなく、2時間以内のソート・アンド・プロセスオプションよりも改善点があります。


しかし、(100回行われる)のは、入力ファイルを介して各時間を扱う百個のファイルを保持し、次のプログラム、で、異なる方針を試してみましょう:これは軽微である

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

#define FOUT_STR "1234_out.dat" 

int main (void) { 
    FILE *fIn, *fOut[100]; 
    char outFile[sizeof (FOUT_STR)]; 
    char buff[1000]; 
    int seg, cust; 
    char segNum[3], custNum[3]; 

    for (seg = 0; seg < 100; seg++) { 
     sprintf (segNum, "%02d", seg); 

     if ((fIn = fopen ("data.dat", "r")) == NULL) { 
      printf ("Error %d opening 'data.dat'\n", errno); 
      return 1; 
     } 

     for (cust = 0; cust < 100; cust++) { 
      sprintf (custNum, "%02d", cust); 

      memcpy (outFile, FOUT_STR, sizeof (FOUT_STR)); 
      memcpy (outFile+0, segNum, 2); 
      memcpy (outFile+2, custNum, 2); 
      if ((fOut[cust] = fopen (outFile, "w")) == NULL) { 
       printf ("Error %d opening '%s'\n", errno, outFile); 
       return 1; 
      } 
     } 

     while (fgets (buff, sizeof (buff), fIn) != NULL) { 
      if (memcmp (&(buff[4]), segNum, 2) == 0) { 
       cust = (buff[6] - '0') * 10 + buff[7] - '0'; 
       fputs (buff, fOut[cust]); 
      } 
     } 

     for (cust = 0; cust < 100; cust++) { 
      fclose (fOut[cust]); 
     } 

     fclose (fIn); 
    } 

    return 0; 
} 

実際に入力ファイルを100回処理するばらつきで、毎回100個の出力ファイルを対象とする行だけを処理します。

これを100Mファイルで実行すると、約28秒(0:00:28)かかります。もう一度、これは200Mと400Mのファイルに対して線形に拡大するように見えるので、70Gファイルは5:26:40になるはずです。

まだ2時間弱の数字にはまだ近くない。100Mファイルの12Sほどかかり

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

#define FOUT_STR "1234_out.dat" 

int main (void) { 
    FILE *fIn, *fOut[1000]; 
    char outFile[sizeof (FOUT_STR)]; 
    char buff[1000]; 
    int seg, cust; 
    char segNum[2], custNum[4]; 

    for (seg = 0; seg < 10; seg++) { 
     sprintf (segNum, "%01d", seg); 

     if ((fIn = fopen ("data.dat", "r")) == NULL) { 
      printf ("Error %d opening 'data.dat'\n", errno); 
      return 1; 
     } 

     for (cust = 0; cust < 1000; cust++) { 
      sprintf (custNum, "%03d", cust); 

      memcpy (outFile, FOUT_STR, sizeof (FOUT_STR)); 
      memcpy (outFile+0, segNum, 1); 
      memcpy (outFile+1, custNum, 3); 
      if ((fOut[cust] = fopen (outFile, "w")) == NULL) { 
       printf ("Error %d opening '%s'\n", errno, outFile); 
       return 1; 
      } 
     } 

     while (fgets (buff, sizeof (buff), fIn) != NULL) { 
      if (memcmp (&(buff[4]), segNum, 1) == 0) { 
       cust = (buff[5] - '0') * 100 + (buff[6] - '0') * 10 + buff[7] - '0'; 
       fputs (buff, fOut[cust]); 
      } 
     } 

     for (cust = 0; cust < 1000; cust++) { 
      fclose (fOut[cust]); 
     } 

     fclose (fIn); 
    } 

    return 0; 
} 

、そして私たちの午前2時20分00秒を与えるだろう近くに来て、私たちが一度千個の出力ファイルを開いたときに


は何が起こります並べ替えが、かなりそこにはありません。私たちは1本のヒットで開放に全体の10000個のファイルをしようと、次の論理的なステップを行くとき


Unfortuntely、我々は、次を参照してください。

Error 24 opening '1020_out.dat' 

我々は最終的には(限界に来ていることを意味します標準出力、標準エラー、およそ1019個のファイルハンドル)、これは1024個のハンドルが私たちが許されているすべてのものであることを示しています。

だから、並べ替えと処理の方法です。

+0

あなたの努力に感謝します。あなたは、私たちに多くのソリューションを提供し、他のソリューションとどのように相互に関連しているのですか。 残念ながら、現在使用されている方法は現時点では最善のようです。 ライセンスコストのためにデータベースソリューションを実装することはできません。高齢者は無料のデータベースを使用できません。 もう一度ありがとうございます。 –

+0

@BharatElwe、可能な解決策を拒否する人たちは馬鹿です(あなたのものではありません、私はあなたの先輩について話しています)。しかし、たぶんあなたは自分のような老人ヤギになってから、馬鹿馬鹿しくして呼ぶことができます:-)私は少なくともDBソリューションを試しています。最速の非DBソリューションが2時間以内に実行する同じ作業を27分で実行できることがわかったら、それをシニアに提示して決定を下します。しかし、少なくともアドバイスをしました。あなたのアドバイスを無視することを選択した場合、それはあなたの問題ではなく、あなたの問題です。 – paxdiablo

0

Unixプラットフォームの制限についてはわかりませんが、WindowsではWINAPIを使用して必要な数のファイルを開くか、_setMaxstdioを使用してオープンファイルハンドルの最大数をデフォルトで512に設定できます(fopen )。あなたは、必ずしも8000ファイルを必要としない

Here's a similar solution which may help you out!

関連する問題