Pregunta:
¿Leer archivos BAM ordenados e indexados más rápido en C ++?
SmallChess
2017-10-13 04:24:58 UTC
view on stackexchange narkive permalink

Tengo algunos archivos BAM grandes de alineación ordenados e indexados. Me gustaría leer todas las lecturas y aplicar una operación personalizada en cada una de ellas. Los pedidos no son importantes. Estoy usando htslib en C ++ para hacer la lectura. Tengo una sola máquina potente.

El problema es ... leer un archivo BAM grande es muy lento incluso en C ++.

P: ¿Hay ¿Algún truco para acelerar la lectura? ¿Leyendo archivos BAM multiproceso (tengo muchos núcleos)?

Si está leyendo el archivo completo, la indexación no ayudará. Eso es para buscar rápidamente regiones específicas en un archivo BAM.
@gringer ¿Puedo acelerar dividiendo el archivo en particiones y luego cada una de ellas mediante subprocesos múltiples? Tengo muchos núcleos para hacer eso.
La lectura de datos es un problema de E / S, no de la CPU. ¿Tiene sus datos en un SSD?
@AlexReynolds Sí. Pero, ¿hay algo más que podamos hacer en programación?
¿Usar rutinas mmap para leer datos, tal vez? Puede que le proporcione un pequeño beneficio sobre la lectura con fopen / fseek / fread, etc. Pero no hay forma de garantizar un beneficio, salvo comparar lo que sea que esté tratando de hacer. Y debe tener cuidado con la purga de cachés entre pruebas. Es fácil para las personas hacer afirmaciones falsas sobre la rapidez de sus herramientas si no están purgando la caché de lectura.
Puede investigar términos como "división" o "E / S paralelas" para beneficiarse de múltiples operaciones de lectura a través del hardware. Tener varios procesadores o núcleos puede ser útil para los algoritmos, pero no creo que sea beneficioso leer datos secuenciales del almacenamiento.
La solución óptima está totalmente determinada por la "operación personalizada" que desea aplicar. ¿Qué quieres hacer exactamente?
¿Qué quieres decir con "muy lento"? ¿Cómo se compara con `time cat > / dev / null`? ¿Cómo se compara con el código cuando no se hace nada con los registros?
Tres respuestas:
Nisba
2018-03-30 21:08:15 UTC
view on stackexchange narkive permalink

En esta respuesta, quiero mostrarle algunos puntos de referencia que comparan tres formas en serie diferentes de leer datos en C ++, siendo la tercera la más rápida.

En el primer método utilizo un std :: istreambuf_iterator , en el segundo leo el archivo línea por línea en un std :: vector y en el tercero utilizo operaciones con sabor a C.

  / * compilar con g ++ -std = c ++ 11 -O2 read_files.cpp -o read_filestest el programa con el siguiente add if = / dev / aleatorio de =. / dummy5GiB bs = 1024 count = $ [1024 * 1024 * 5] para i en 0 1 2; hacer tiempo ./reading_files dummy5GiB $ i; done * / # include # include <fstream> <iostream> # include # include <vector> <cstring> // para STRCMP # include # include <string> <algorithm> // para minusing namespace std;! int main (int argc, char * argv []) {if (argc = 3) {cerr << "uso: read_files <path> <number 0 o 1 o 2> \ n"; return 1; } archivo ifstream (argv [1], ios :: in | ios :: binary); if (! file.is_open ()) {cerr << argv [1] << "archivo no abierto! \ n"; return 1; } cout << "--------------------------- \ n"; if (! strcmp (argv [2], "0")) {cout << "método 0 \ n"; vector<char> content ((std :: istreambuf_iterator<char> (archivo)), std :: istreambuf_iterator<char> ()); cout << content.size () << "bytes leídos \ n"; } else if (! strcmp (argv [2], "1")) {cout << "método 1 \ n"; línea de cuerda; vector<string> content; while (getline (archivo, línea)) {content.push_back (línea); }
cout << content.size () << "líneas leídas \ n"; } else if (! strcmp (argv [2], "2")) {cout << "método 2 \ n"; file.seekg (0, std :: ios :: end); unsigned long long file_size = file.tellg (); cout << "el tamaño del archivo es" << file_size << "\ n"; // máximo 5 GiB de memoria, para que quepa en mi 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)); unsigned long long procesado = 0; while (! file.eof ()) {file.read (buffer, block_size); cout << "leer un bloque de" << min (tamaño_archivo - procesado, tamaño_bloque) << "bytes \ n"; // trabaje aquí con el bloque en la memoria (si el tamaño del bloque es > 0) procesado + = block_size; } libre (búfer); } archivo.close (); return 0;}  

En el tercer método leo el archivo en bloques de 5GiB, para que quepa en la RAM libre de mi máquina.

Aquí está el punto de referencia de el programa en un archivo de 5 GiB generado aleatoriamente.

  bash-3.2 $ for i in 0 1 2; hacer tiempo ./reading_files dummy5GiB $ i; hecho --------------------------- método 05368709120 bytes readreal 0m35.194suser 0m19.665ssys 0m12.999s --------- ------------------ método 120973135 líneas readreal 0m40.478suser 0m34.046ssys 0m5.870s ------------------- -------- método 2 el tamaño del archivo es 5368709120 leer un bloque de 5368709120 bytes leer un bloque de 0 bytes real 0m4.413suser 0m1.757ssys 0m2.617s  

Aquí está el punto de referencia en un archivo .bam real de 32GiB utilizando solo el tercer método, aquí la división del archivo leído juega un papel fundamental. No puedo decirte cuán grande es porque la memoria de mi disco duro se satura antes del final de la ejecución y entonces tengo que matar el programa.

  time ./reading_files ./HG00252.mapped. ILLUMINA.bwa.GBR.low_coverage.20130415.bam 2 --------------------------- método 2 el tamaño del archivo es 34316054058 leer un bloque de 5368709120 bytes leer un Leer un bloque de 5368709120 bytes leer un bloque de 5368709120 bytes leer un bloque de 5368709120 bytes leer un bloque de 5368709120 bytes leer un bloque de 5368709120 bytes leer un bloque de 2103799338 bytes real 0m50.656suser 0m9.135ssys 0m35.728s  

El El punto clave es minimizar las operaciones de archivo leyendo grandes bloques de datos a la vez (siempre que quepan en la memoria) y operar sobre ellos.

El uso de mmap podría conducir a una mejora , pero no creo que sea tan grande como usar el tercer método en lugar de los dos primeros.

¿Por qué su "método 2" utiliza una gestión de memoria manual gratuita (estilo C!) En lugar de un vector? - Tampoco estoy seguro de cuánto ayuda esto realmente a OP, ya que la pregunta es sobre el análisis de BAM, y hasta donde yo sé, ni Staden IO ni htslib admiten la lectura de un búfer personalizado (pero deberían realizar su propio almacenamiento en búfer de todos modos).
Christopher Chang
2018-03-30 21:47:21 UTC
view on stackexchange narkive permalink

Si necesita recorrer todo el archivo BAM, la descompresión suele ser el principal cuello de botella. Tres cosas que puede hacer:

  • Instale libdeflate ( https://github.com/ebiggers/libdeflate) y luego la versión de desarrollo de htslib, y asegúrese de que Este último está compilado para usar libdeflate. En mi experiencia, esto duplica aproximadamente la eficiencia de la descompresión.
  • Llame a la función bgzf_mt () de htslib justo después de abrir el archivo para su lectura, para solicitar múltiples subprocesos del descompresor. Por lo general, voy con 2-6, dependiendo de lo que esté haciendo realmente con las lecturas.
  • Tener un hilo de lectura / descompresión anticipada separado que realice las llamadas bam_read1 (). Idealmente, cuando su hilo principal está procesando la lectura #X, los hilos en segundo plano ya están trabajando en la descompresión de lecturas, p. (X + 100) a (X + 200).

Además, si su carga de trabajo es "vergonzosamente paralela", también puede beneficiarse al iniciar múltiples procesos simultáneamente, configurando cada uno para manejar un proceso similar trozo de tamaño grande del BAM.

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

Tanto en deepTools como en MethylDackel, implementamos subprocesos múltiples haciendo que los subprocesos individuales procesen regiones genómicas independientes. En algún momento, alcanza un límite de IO, pero a menos que su procesamiento sea trivial, IO normalmente no se limitará con aplicaciones de un solo subproceso. Como ya ha ordenado / indexado archivos BAM, puede aprovechar la misma estrategia.



Esta pregunta y respuesta fue traducida automáticamente del idioma inglés.El contenido original está disponible en stackexchange, a quien agradecemos la licencia cc by-sa 3.0 bajo la que se distribuye.
Loading...