2016-09-20 16 views
0

私はC++で非常に新しいです!それで、あなたがそれを考慮し、可能な限り簡単に答えることができたら、本当に感謝しています。私は> 40000シーケンス(500Mb近く)でfastaファイルを解析し、IDとシーケンス長を新しいファイルに書き込む必要があります。私はそれがC++で非常に遅くなっていることを発見しました。この目的のために、Pythonははるかに高速に動作します。しかし、私はC++でどのようにできるのかを知る必要があります。私はこのプロセスをC++に固定する方法があるのだろうか?大きなファイルを読み書きする

これは私のコードです:事前に

#include <iostream> 
#include <fstream> 
#include <string> 
#include <time.h> 
#include <stdio.h> 

using namespace std; 
int main() { 
    time_t start, end; 
    time(&start); 
    clock_t begin = clock(); 
    ifstream file; 
    string line; 
    string id; 
    string content; 
    int len = 0; 
    int i = 0; 
    ofstream out; 

    file.open("contigs.fasta", ios::in); 
    out.open("output.txt", ios::out); 
    while (getline(file, line)) { 
     if (line[0] == '>') { 
      i++; 
      if (i != 1) { 
      //cout << id << "\n" << len << "\n" << content << endl; 

       //out.write(line.c_str(), line.size()); 
      out << id << " : " << len << endl; 
      } 
      id = line; 
      len = 0; 
      content = ""; 
     } 
     else 
     { 
      len += line.length(); 
      content += line; 
     } 
    } 
    //cout << id << "\n" << len << "\n" << content << endl; 
    //out << id << " : " << len << endl; 
    cout << "Total number of sequences :" << i << "\n"; 
    out.close(); 
    time (&end); 
double dif = difftime (end,start); 
printf ("Elasped time is %.2lf seconds.", dif); 
    return 0; 
} 

ありがとう!

+0

ファイル全体を一度に読むのではなく、小さな塊で読む。 http://stackoverflow.com/questions/20911584/how-to-read-a-file-in-multiple-chunks-until-eof-c – Oscar

+0

'content + = line;'もちろん、遅いです。メモリの再割り当て、コンテンツのコピー、新しい行の追加、これには時間がかかります。オスカー氏のように、そのアプローチを再考したいかもしれません。 –

+1

'out << id <<": "<< len << endl;' endl'は単に新しい行を与えるだけではありません。また、バッファをフラッシュします。必要がない場合は使用しないでください。 '\ n'を使うだけです。 – Matt

答えて

1

なぜ低速ですか?

fastaファイルがかなり大きくなる可能性があります。しかし、それはC++の問題ではありません。 最もよく知るには、プロファイラを使用する方法があります。

しかし、ここでは、文字列の割り当ては非常に良い根本的な原因です。文字列の最後にすべての行読み取りが追加され、文字列が大きくなります。これは、contentの成長のために頻繁に再割り当てされることを意味し、割り当て、コピー、メモリの割り当て解除などを必要以上に多くします。

このようなアプローチでは、ヒープフラグメンテーションが発生し、数十万回実行するとプロセスがかなり遅くなる可能性があります。幸いにも、これをより速く行ういくつかの戦略があります。

どのように高速化するのですか?

reserve()を使用して、contentの空きスペースを事前に割り当てることができます。これは、特にヌクレオチドの平均サイズを知っている場合、簡単なアクセラレータになります。しかし、あなたがそうしなくても、それは再配分の努力の多くを減らすことができます。

差があるかどうだけを観察するためにこれを試してみてください。

content.reserve (100000); // just before entering into the loop. 

さらにそれを高速化する方法は?

にも非常に有効であることができる別のアプローチは、seekg()tellg()であなたのFASTAファイルのサイズを決定することです、それは直接あなた、その後fread()と単一の読み取りでメモリ内のファイルをロードし、解析/プロセスそれを読んだ。

この非常に未加工のアプローチでは、Gb/sの範囲でスループットを取得する必要があります。

最後に、パフォーマンス測定のためにリリースモード(オプティマイザがオン)でC++コードをコンパイルすることを忘れないでください。

+0

お返事ありがとう、ご意見ありがとうございました! –

+0

@tov_Kirovよろしくお願いします!好奇心から:あなたは何を最終的にやったのですか?あなたが今観察しているスピードは何ですか? – Christophe

2

ファイル全体またはブロックを事前に割り当てられた文字列に読み込む必要があります。そして、必要に応じてstd::stringstreamを使用してファイルを処理します。ここでは自分のプログラムで使用する例を示します。私のファイルはそれほど大きくはありませんが、数千の行が含まれていて、それぞれが特定の文字やコピーなどのために解析されます。そして、これにはわずかな時間がかかります(最大ファイルの場合は約50ms、読み込みと解析)。

//1- read the file 
std::string str; // allocate string 
{ 
    //compute file size 
    int iFileSize = 0; 
    { 
     std::ifstream ifstr(rkFilename.c_str(), std::ios::binary); // create the file stream - this is scoped for destruction  

     if(!ifstr.good()) 
     { 
      return; 
     } 

     //get the file size 
     iFileSize = ifstr.tellg(); 
     ifstr.seekg(0, std::ios::end); // open file at the end to get the size 
     iFileSize = (I32) ifstr.tellg() - iFileSize; 
    } 

    //reopen the file for reading this time 
    std::ifstream ifstr(rkFilename.c_str()); 

    //create a char* with right size 
    char* pcFileBuffer = new char[iFileSize]; 

    //copy the full file in there 
    ifstr.read(pcFileBuffer, iFileSize); 

    //put it all into a string - could be optimised I guess 
    str = std::string(pcFileBuffer); 

    //bookeeping 
    delete[] pcFileBuffer; 
    pcFileBuffer = NULL; 
} 

// create a stream using the allocated string 
// this stream works as a file reader basically so you can extract lines into string, etc... 
std::stringstream filebuf(str); 

//the rest is up to you 

あなたがメモリにフル500MBのファイルを読み込むための十分なスペースがない場合... chuncksを読むためにあなたができる

つ以上の最適化を、これを調整します。 @Adrianが言ったように、content += lineはかなり遅いです...あなたのコードを見て、開始と停止のインデックスを保存しながら、データをコピーしないで'>'文字を探したいかもしれません。次に、メモリを1回だけ割り当て、見つかった開始インデックスと停止インデックスを使用してデータをコピーします(または開始インデックスと停止インデックスのデータ構造を作成します:-))。それが私のファイルを解析するために使うものです。私はstd::stringfind_first_of,find_first_not_of,find_last_ofおよびsubstrの方法を使用します。これらはおそらく最適ではありませんが、コードを読めるように保ち、私の目的にとっては十分速いものです。

私の答えはあなたに何をすべきかを示唆し、プログラムのスピードアップに役立つことを願っています。

また、プロファイラーを使用して、最も時間を要するものを特定することをお勧めします。たとえば、Visual Studio 2015のネイティブです。あなたはout << ... << endlを使用している

敬具

1

。これは、単一の行を直接ディスクにフラッシュします。ディスクは文字指向ではないので、読み取り - 変更 - 書き込み操作を意味します。

代わりにout << '\n'を使用して、と書いてください。改行はとなります。ディスクキャッシュがこれを処理します。

関連する問題