2016-09-20 6 views
0

Ich bin sehr neu in C++! Daher würde ich es wirklich schätzen, wenn Sie es in Betracht ziehen und so einfach wie möglich antworten. Ich muss Fasta-Datei mit> 40000 Sequenzen (in der Nähe von 500 MB) analysieren und ID und Sequenzlänge in die neue Datei schreiben. Ich habe festgestellt, dass es in C++ sehr langsam geht und Python zu diesem Zweck viel schneller arbeitet. Aber ich muss lernen, wie ich es in C++ machen kann. Ich frage mich, gibt es irgendwelche Möglichkeiten, diesen Prozess für C++ zu befestigen?C++ lesen/schreiben große Dateien

Dies ist mein Code:

#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; 
} 

Vielen Dank im Voraus!

+0

Lesen Sie es in kleinen Stücken statt ganze Datei auf einmal zu lesen. Siehe http://stackoverflow.com/questions/20911584/how-to-read-a-file-in-multiple-chunks-until-eof-c – Oscar

+0

'content + = line;' Das ist natürlich langsam. Speicher neu zuordnen, Inhalt kopieren, die neue Zeile anhängen, das braucht Zeit. Vielleicht möchten Sie diesen Ansatz noch einmal überdenken, wie Oscar sagte. –

+1

'out << id <<": "<< len << endl;' 'endl' macht mehr als nur eine neue Zeile. Es spült auch den Puffer. Benutze es nicht, wenn du es nicht brauchst. Verwenden Sie einfach '\ n'. – Matt

Antwort

1

Warum ist es langsam?

Eine fasta Datei kann ziemlich groß sein. Aber das ist in C++ kein Problem. Der beste Weg zu wissen wäre, einen Profiler zu verwenden.

Aber hier ist String-Zuweisung ein sehr guter Kandidat Ursache: jede Zeile gelesen wird am Ende der Zeichenfolge hinzugefügt, wodurch die Zeichenfolge zu wachsen. Dies bedeutet häufige Neuzuweisung wegen content 's Wachstum, die Zuteilung, Kopieren, Freigabe von Speicher und viel mehr als erforderlich verursacht!

Ein solcher Ansatz kann eine Heap-Fragmentierung verursachen und den Prozess beträchtlich verlangsamen, wenn er mehrere hunderttausend Mal durchgeführt wird. Glücklicherweise gibt es mehrere Strategien, dies schneller zu tun.

Wie man es schnell beschleunigt?

Sie können reserve() verwenden, um Speicherplatz für content vorab zuzuweisen. Dies kann ein einfacher Beschleuniger sein, besonders wenn Sie die durchschnittliche Größe Ihres Nukleotids kennen. Aber selbst wenn Sie dies nicht tun, kann dies die Umverteilungsbemühungen erheblich reduzieren.

Genau dies versuchen zu beobachten, wenn ein Unterschied gibt es:

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

Wie es beschleunigen weiter?

Ein weiterer Ansatz, der ist auch sehr effektiv sein kann, um die Größe Ihrer fasta-Datei mit seekg() und tellg(), dann laden Sie die Datei im Speicher in einem einzigen Lesevorgang mit fread() und parsen/Prozess direkt, wo Sie zu bestimmen, hab es gelesen.

Mit diesem sehr rohen Ansatz sollten Sie einen Durchsatz im Gb/s-Bereich erreichen.

Last but not least, Vergessen Sie nicht, Ihren C++ - Code im Freigabemodus (Optimizer ein) für Leistungsmessungen zu kompilieren.

+0

Vielen Dank für Ihre Antworten und Kommentare! Sie haben mir sehr geholfen! –

+0

@tov_Kirov du bist willkommen! Aus Neugierde: Was hast du endlich gemacht und wie schnell beobachtest du jetzt? – Christophe

2

Vielleicht sollten Sie die ganze Datei oder einen Block davon in eine vordefinierte Zeichenfolge lesen. Und dann verwenden Sie eine std::stringstream, um die Datei nach Bedarf zu verarbeiten: Hier ist ein Beispiel dafür, was ich in meinen Programmen verwende. Meine Dateien sind nicht so groß, aber sie enthalten Tausende von Zeilen, von denen jede für bestimmte Zeichen analysiert, kopiert usw. wird. Und dies dauert nur ein paar Millisekunden (etwa 50 ms für die größten Dateien, Laden und Parsen).

//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 

diese Anpassung ein chuncks zu lesen, wenn Sie eine volle 500 MB-Datei in Ihrem Speicher nicht genügend Platz haben ...

One man tun könnte mehr Optimierung zu lesen. Wie @Adrian sagte, die content += line ist ziemlich langsam ... Blick auf Ihren Code, möchten Sie vielleicht nach der '>' Zeichen beim Speichern von Start und Stopp Indizes suchen, während nicht Daten zu kopieren. Sie würden dann den Speicher nur einmal allokieren und Daten mit Hilfe der gefundenen Start- und Stoppindizes herumkopieren (oder einfach eine Datenstruktur von Start- und Stoppindizes erstellen :-)). Das ist es, was ich benutze, um meine Dateien zu parsen. Ich verwende std::stringfind_first_of, find_first_not_of, find_last_of und substr Methoden. Während diese wahrscheinlich suboptimal sind, halten sie den Code lesbar und sind schnell genug für meinen Zweck.

Ich hoffe meine Antwort gibt Ihnen einen Hinweis, was zu tun ist, und dass es Ihnen hilft, Ihr Programm zu beschleunigen.

Auch ist es eine gute Idee, einen Profiler zu verwenden, um festzustellen, was Sie am meisten Zeit braucht. Es ist zum Beispiel in Visual Studio 2015 nativ.

Mit freundlichen Grüßen

1

Sie out << ... << endl verwenden. Dadurch wird die einzelne Zeile direkt auf die Festplatte geschrieben. Da Platten nicht zeichenorientiert sind, bedeutet dies eine Lese-Modifizier-Schreib-Operation.

Verwenden Sie stattdessen out << '\n', um nur eine Newline zu schreiben. Der Plattencache wird das handhaben.