Frage:
Sortierte und indizierte BAM-Dateien in C ++ schneller lesen?
SmallChess
2017-10-13 04:24:58 UTC
view on stackexchange narkive permalink

Ich habe einige sortierte und indizierte große BAM-Ausrichtungsdateien. Ich möchte alle Lesevorgänge lesen und auf jeden einen benutzerdefinierten Vorgang anwenden. Die Bestellungen sind nicht wichtig. Ich verwende htslib in C ++, um das Lesen durchzuführen. Ich habe eine einzige leistungsstarke Maschine.

Das Problem ist ... das Lesen einer großen BAM-Datei ist selbst in C ++ sehr langsam.

F: Ist vorhanden Gibt es einen Trick, um das Lesen zu beschleunigen? Lesen von BAM-Dateien mit Multithread (ich habe viele Kerne)?

Wenn Sie die gesamte Datei lesen, hilft die Indizierung nicht. Dies dient dazu, schnell nach bestimmten Regionen in einer BAM-Datei zu suchen.
@gringer Kann ich beschleunigen, indem ich die Datei in Partitionen aufteile, dann jede durch Multithreading? Ich habe viele Kerne, um das zu tun.
Das Lesen von Daten ist ein E / A-Problem, kein CPU-Problem. Haben Sie Ihre Daten auf einer SSD?
@AlexReynolds Ja. Aber können wir noch etwas programmieren?
Vielleicht mit mmap-Routinen Daten lesen? Es könnte Ihnen einen kleinen Vorteil gegenüber dem Lesen mit fopen / fseek / fread usw. verschaffen. Es gibt jedoch keine Möglichkeit, einen Vorteil zu garantieren, außer dem Benchmarking, was auch immer Sie tun möchten. Und Sie müssen vorsichtig sein, wenn Sie zwischen den Tests Caches leeren. Es ist für Leute einfach, falsche Behauptungen darüber aufzustellen, dass ihre Tools schnell sind, wenn sie den Lese-Cache nicht leeren.
Sie können Begriffe wie "Striping" oder "Parallel I / O" untersuchen, um von mehreren Lesevorgängen über Hardware zu profitieren. Das Vorhandensein mehrerer Prozessoren oder Kerne kann für Algorithmen nützlich sein, aber ich denke nicht, dass das Lesen sequentieller Daten aus dem Speicher von Vorteil ist.
Die optimale Lösung wird vollständig durch die "benutzerdefinierte Operation" bestimmt, die Sie anwenden möchten. Was möchten Sie genau tun?
Was meinst du mit "sehr langsam"? Wie ist der Vergleich mit `time cat > / dev / null`? Wie vergleicht es sich mit dem Code, wenn mit den Datensätzen nichts gemacht wird?
Drei antworten:
Nisba
2018-03-30 21:08:15 UTC
view on stackexchange narkive permalink

In dieser Antwort möchte ich Ihnen einige Benchmarks zeigen, die drei verschiedene serielle Methoden zum Lesen von Daten in C ++ vergleichen, wobei die dritte die schnellste ist.

Bei der ersten Methode verwende ich einen std :: istreambuf_iterator , bei der zweiten lese ich die Datei zeilenweise in einen std :: vector und im dritten verwende ich Operationen mit C-Geschmack.

  / * kompiliere mit g ++ -std = c ++ 11 -O2 Reading_files.cpp -o Reading_filestest das Programm mit dem folgendendd if = / dev / zufällig von =. / dummy5GiB bs = 1024 count = $ [1024 * 1024 * 5] für i in 0 1 2; Zeit nehmen ./reading_files dummy5GiB $ i; getan * / # include <fstream> # include <iostream> # include <vector> # include <cstring> // für strcmp # include <string> # include <algorithm> // für minusing namespace std;! int main (int argc, char * argv []) {if (argc = 3) {cerr << "Verwendung: Lesedateien <path> <number 0 oder 1 oder 2> \ n"; return 1; } ifstream-Datei (argv [1], ios :: in | ios :: binary); if (! file.is_open ()) {cerr << argv [1] << "Datei nicht geöffnet! \ n"; return 1; } cout << "--------------------------- \ n"; if (! strcmp (argv [2], "0")) {cout << "method 0 \ n"; vector<char> content ((std :: istreambuf_iterator<char> (Datei)), std :: istreambuf_iterator<char> ()); cout << content.size () << "Bytes gelesen \ n"; } else if (! strcmp (argv [2], "1")) {cout << "Methode 1 \ n"; String Line; vector<string> Inhalt; while (getline (Datei, Zeile)) {content.push_back (Zeile); }}
cout << content.size () << "Zeilen gelesen \ n"; } else if (! strcmp (argv [2], "2")) {cout << "Methode 2 \ n"; file.seekg (0, std :: ios :: end); unsigned long long file_size = file.tellg (); cout << "Dateigröße ist" << Dateigröße << "\ n"; // maximal 5 GiB Speicher, also passend zu meinem RAM unsigned long long max_block_size = 1024ULL * 1024ULL * 1024ULL * 5; file.clear (); file.seekg (0, std :: ios :: beg); unsigned long long block_size = min (max_block_size, file_size); char * buffer = (char *) malloc (block_size * sizeof (char)); vorzeichenlos lang lang verarbeitet = 0; while (! file.eof ()) {file.read (buffer, block_size); cout << "lese einen Block von" << min (Dateigröße - verarbeitet, Blockgröße) << "Bytes \ n"; // arbeite hier mit dem Block im Speicher (wenn die Blockgröße > 0 ist) verarbeitet + = block_size; } free (Puffer); } file.close (); return 0;}  

Bei der dritten Methode habe ich die Datei in Blöcken von 5 GB gelesen, um sie an den freien Arbeitsspeicher meines Computers anzupassen.

Hier ist der Benchmark von das Programm in einer zufällig generierten 5-GiB-Datei.

  bash-3.2 $ für i in 0 1 2; Zeit nehmen ./reading_files dummy5GiB $ i; erledigt --------------------------- Methode 05368709120 Bytes readreal 0m35.194suser 0m19.665ssys 0m12.999s --------- ------------------ Methode 120973135 Zeilen readreal 0m40.478suser 0m34.046ssys 0m5.870s ------------------- -------- Methode 2Dateigröße ist 5368709120Lesen Sie einen Block von 5368709120 Bytesread Einen Block von 0 Bytesreal 0m4.413suser 0m1.757ssys 0m2.617s  

Hier ist der Benchmark für eine echte 32-GB-BAM-Datei, bei der nur die dritte Methode verwendet wird. Hier spielt die Aufteilung der gelesenen Datei eine grundlegende Rolle. Ich kann Ihnen nicht sagen, wie groß mein Festplattenspeicher vor dem Ende der Ausführung ist und ich das Programm beenden muss.

  time ./reading_files ./HG00252.mapped. ILLUMINA.bwa.GBR.low_coverage.20130415.bam 2 --------------------------- Methode 2Die Dateigröße beträgt 34316054058Lesen Sie einen Block mit 5368709120 Bytesread a Block von 5368709120 Bytesread ein Block von 5368709120 Bytesread ein Block von 5368709120 Bytesread ein Block von 5368709120 Bytesread ein Block von 5368709120 Bytesread ein Block von 2103799338 Bytesreal 0m50.656suser 0m9.135ssys 0m35.728s  

Der wichtigste Punkt besteht darin, die Dateivorgänge zu minimieren, die große Datenblöcke gleichzeitig lesen (solange sie in den Speicher passen) und sie zu bearbeiten.

Die Verwendung von mmap kann zu einer Verbesserung führen , aber ich denke nicht, dass es so groß wäre wie die dritte Methode anstelle der ersten beiden.

Warum verwendet Ihre „Methode 2“ anstelle eines Vektors eine kostenlose manuelle Speicherverwaltung (im C-Stil!)? - Ich bin mir auch nicht sicher, wie sehr dies OP tatsächlich hilft, da es um das Parsen von BAM geht. Soweit ich weiß, unterstützt weder Staden IO noch htslib das Lesen aus einem benutzerdefinierten Puffer (sollte aber trotzdem eine eigene Pufferung durchführen).
Christopher Chang
2018-03-30 21:47:21 UTC
view on stackexchange narkive permalink

Wenn Sie die gesamte BAM-Datei durchlaufen müssen, ist die Dekomprimierung normalerweise der Hauptengpass. Drei Dinge, die Sie tun können:

  • Installieren Sie libdeflate ( https://github.com/ebiggers/libdeflate) und dann die Entwicklungsversion von htslib Letzteres ist für die Verwendung von libdeflate kompiliert. Dies verdoppelt meiner Erfahrung nach die Dekomprimierungseffizienz in etwa.
  • Rufen Sie die Funktion bgzf_mt () von htslib direkt nach dem Öffnen der Datei zum Lesen auf, um mehrere Dekomprimierungsthreads anzufordern. Ich gehe im Allgemeinen mit 2-6, abhängig davon, was ich tatsächlich mit den Lesevorgängen mache.
  • Lassen Sie die Aufrufe bam_read1 () von einem separaten Thread zum Lesen / Dekomprimieren ausführen. Wenn Ihr Hauptthread Lesevorgang #X verarbeitet, arbeiten Hintergrundthreads bereits daran, Lesevorgänge zu dekomprimieren, z. (X + 100) bis (X + 200).

Wenn Ihre Arbeitslast "peinlich parallel" ist, können Sie auch mehrere Prozesse gleichzeitig starten und jeweils so konfigurieren, dass sie ähnlich ablaufen -großer Teil der BAM.

Devon Ryan
2017-10-13 11:39:21 UTC
view on stackexchange narkive permalink

Sowohl in deepTools als auch in MethylDackel implementieren wir Multithreading, indem einzelne Threads separate Genomregionen verarbeiten. Irgendwann haben Sie ein E / A-Limit erreicht, aber wenn Ihre Verarbeitung nicht trivial ist, wird IO bei Single-Threaded-Anwendungen normalerweise nicht eingeschränkt. Da Sie bereits BAM-Dateien sortiert / indexiert haben, können Sie dieselbe Strategie nutzen.



Diese Fragen und Antworten wurden automatisch aus der englischen Sprache übersetzt.Der ursprüngliche Inhalt ist auf stackexchange verfügbar. Wir danken ihm für die cc by-sa 3.0-Lizenz, unter der er vertrieben wird.
Loading...