• Publicidad

Rendimiento

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Rendimiento

Notapor Stoner » 2012-03-15 05:47 @282

Hola perleros. Tengo un problema de rendimiento. El script hace lo que yo quiero, pero en un tiempo inadmisible.

Aquí os muestro el problema.
Tengo dos archivos (indico los saltos de línea con \n):

File1:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
1071711 88215   CCATAATTCGCAAAACAAACTCCTTCATTTCCAGACATGTATCTTTACCTCTCCTTATTCTCGCTTTCTGTACTACTTGTTATGCTGGCATATCGTTACCGCGCCGCCGTCGTACCGCACATCCCAGCCCGCGTGCGA... CCATAATTCGCAAAACAAACTCCTTCATTTCCAGACATGTATCTTTACCTCTCCTTATTCTCGCTTTCTGTACTACTTGTTATGCTGACATATCGTTACCGCGCCGCCGTCGTACCGCACATCCCAGCCCGCGTGCGAGC...  609      609     scaffold_06     1563627 1564396 +       769     chr8    722778  723547  -       769\n
1108185 59382   ATGGCCTTCGATCTGCCGCCTTTACGGATTGCTCTGTACTTTACTTTGGGCTTATTCTCGTTCATACTCTTCTGTCTCAGCGCAGCGAGGATACACTACACAACCCATCTCCCTGCGGGCGATCCTCTGAATGGGGGGCAC...        ATGGCCTTCGATCTGCCGCCTCTGCGGATTGCTCTGTATTTTACTTTGGGCTTATTTTCGTTCATACTCTTCTGTCTCAGCGCAGCGAGGATACACTACACAACCCATCTCCCTGCGGGCGATCCTCTGAATGGGGGGCAC...  567   567     scaffold_09     447625  448516  +       891     chr7    466963  467854  +       891\n
1074848 85420   ATGCAAGCACATCTAATTTCGGGCACATCTTATTGGGGTCAGAATTCCTATGGAGCTACTCATTCCGATGTCGCGAACTTCCAGAAACCAATATCCTTCTACTGCCAGGACGATTCCATCGATGTATTTCCTATCTCGTTCT...       ATGCAAGAACATCTAATTTCGGGCACATCTTATTGTGGTCAGAATTCCTATGGAGCTACTCATTCCGATGTCGCGAACTTCCAGAAACCAATATCCTTCTACTGCCAGGACGATTCCATCGATGTATTTCCTATCTCGTTCT...       1179    1179    scaffold_03     1744446 1747087 +       2641    chr4    669602  672243  +       2641\n
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


File2 (archivo de varios gigas):
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
468_1405_1416_F5-BC     137     scaffold_01     2335    0       35M     *       0       0       GGTCGGAGTTGTGATGTATTGTAACTGTGAGAAAA     ]]bPPF8RZZM9BU^bXX]TW_KAGPY,0\7-177     NM:i:0  NH:i:21 CC:Z:=  CP:i:3381       XS:A:-\n
1475_1998_1022_F5-BC    137     scaffold_01     2787    0       35M     *       0       0       GTAGGAATTTGATCAGTATACTCTCCTAACATGCC     IILNHJXQLS[^UMIQ^YLNIFSJ)3D/!!)!!11     NM:i:0  NH:i:15 CC:Z:scaffold_03        CP:i:10804      XS:A:-\n
379_369_1975_F3 89      scaffold_01     2941    0       50M     *       0       0       AACAAGCCGATTACCCATCAGTAGACCCTAACGGAGGTGTGTGAAGTTGT      II_^RN1!+UBJRCER^_IAVWZYS\_`aa\SW[]]]\]aZSZcbbc^ZZ      NM:i:0  NH:i:9  CC:Z:=  CP:i:4829083    XS:A:-\n
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Mi script es el siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. open( FILE1,  '<',  'File1' );
  7. open( FILE2,  '<',  'File2' );
  8. open( OUTPUT, '>>', 'Output' );
  9.  
  10. my %hash1;
  11. my %hash2;
  12. my %hash3;
  13.  
  14. while ( my $line1 = <FILE1> )          # Recorro el primer archivo
  15. {
  16.     chomp $line1;
  17.     my @linea        = split( /\t/, $line1 );
  18.     my $PC15         = $linea[0];
  19.     my $PC9          = $linea[1];
  20.     my $seqPC15      = $linea[2];
  21.     my $seqPC9       = $linea[3];
  22.     my $scaffoldPC15 = $linea[6];
  23.     my $startPC15    = $linea[7];
  24.     my $endPC15      = $linea[8];
  25.     my $sensePC15    = $linea[9];
  26.     my $scaffoldPC9  = $linea[11];
  27.     my $startPC9     = $linea[12];
  28.     my $endPC9       = $linea[13];
  29.     my $sensePC9     = $linea[14];
  30.     my @array        = (
  31.         $PC9,       $seqPC15,     $seqPC9,   $scaffoldPC15, $startPC15, $endPC15,
  32.         $sensePC15, $scaffoldPC9, $startPC9, $endPC9,       $sensePC9
  33.     );
  34.     $hash1{$PC15} = [@array];          # y creo mi primer hash
  35. }
  36.  
  37. while ( my $line2 = <FILE2> )          # Recorro el segundo archivo
  38. {
  39.     chomp $line2;
  40.     my @linea2       = split( /\t/, $line2 );
  41.     my $lect         = $linea2[0];
  42.     my $scaffoldlect = $linea2[2];
  43.     my $poslect      = $linea2[3];
  44.     my $cigar        = $linea2[5];
  45.     my $seqlect      = $linea2[9];
  46.     my @array2       = ( $lect, $scaffoldlect, $poslect, $cigar, $seqlect );
  47.     $hash2{$lect} = [@array2];         # Creo el segundo hash
  48. }
  49.  
  50. while ( ( my $IDPC15, my $valor ) = each %hash1 )    # Recorro el primer hash
  51. {
  52.     my $PC9          = $hash1{$IDPC15}[0];
  53.     my $seqPC15      = $hash1{$IDPC15}[1];
  54.     my $seqPC9       = $hash1{$IDPC15}[2];
  55.     my $scaffoldPC15 = $hash1{$IDPC15}[3];
  56.     my $startPC15    = $hash1{$IDPC15}[4];
  57.     my $endPC15      = $hash1{$IDPC15}[5];
  58.     my $sensePC15    = $hash1{$IDPC15}[6];
  59.     my $scaffoldPC9  = $hash1{$IDPC15}[7];
  60.     my $startPC9     = $hash1{$IDPC15}[8];
  61.     my $endPC9       = $hash1{$IDPC15}[9];
  62.     my $sensePC9     = $hash1{$IDPC15}[10];
  63.     my $vecesPC15    = 0;
  64.     my $vecesPC9     = 0;
  65.     while ( ( my $llave, my $valor2 ) = each %hash2 )    # Doble bucle sobre el segundo hash
  66.     {
  67.         my $lect         = $hash2{$llave}[0];
  68.         my $scaffoldlect = $hash2{$llave}[1];
  69.         my $poslect      = $hash2{$llave}[2];
  70.         my $cigar        = $hash2{$llave}[3];
  71.         my $seqlect      = $hash2{$llave}[4];
  72.         if (    ( $scaffoldPC15 eq $scaffoldlect )
  73.             and ( $startPC15 < $poslect )
  74.             and ( $endPC15 > ( $poslect + 50 ) )
  75.             and ( $seqPC15 =~ $seqlect )
  76.             and ( $seqPC9 !~ $seqlect ) ) {
  77.             $vecesPC15++;
  78.             my @nuevoarray3 = (
  79.                 $PC9,         $seqPC15,  $seqPC9, $scaffoldPC15, $startPC15, $endPC15, $sensePC15,
  80.                 $scaffoldPC9, $startPC9, $endPC9, $sensePC9,     $vecesPC15, $vecesPC9
  81.             );
  82.             delete $hash3{$IDPC15};
  83.             $hash3{$IDPC15} = [@nuevoarray3];
  84.         }
  85.         elsif ( ( $scaffoldPC15 eq $scaffoldlect )
  86.             and ( $startPC15 < $poslect )
  87.             and ( $endPC15 > ( $poslect + 50 ) )
  88.             and ( $seqPC9 =~ $seqlect )
  89.             and ( $seqPC15 !~ $seqlect ) ) {
  90.             $vecesPC9++;
  91.             my @nuevoarray3 = (
  92.                 $PC9,         $seqPC15,  $seqPC9, $scaffoldPC15, $startPC15, $endPC15, $sensePC15,
  93.                 $scaffoldPC9, $startPC9, $endPC9, $sensePC9,     $vecesPC15, $vecesPC9
  94.             );
  95.             delete $hash3{$IDPC15};
  96.             $hash3{$IDPC15} = [@nuevoarray3];
  97.         }
  98.         else {
  99.             my @nuevoarray3 = (
  100.                 $PC9,         $seqPC15,  $seqPC9, $scaffoldPC15, $startPC15, $endPC15, $sensePC15,
  101.                 $scaffoldPC9, $startPC9, $endPC9, $sensePC9,     $vecesPC15, $vecesPC9
  102.             );
  103.             delete $hash3{$IDPC15};
  104.             $hash3{$IDPC15} = [@nuevoarray3];
  105.         }
  106.     }
  107. }
  108.  
  109. while ( ( my $IDPC15, my $valor ) = each %hash3 ) {
  110.     print OUTPUT
  111.         "$IDPC15\t$hash3{$IDPC15}[0]\t$hash3{$IDPC15}[1]\t$hash3{$IDPC15}[2]\t$hash3{$IDPC15}[3]\t$hash3{$IDPC15}[4]\t$hash3{$IDPC15}[5]\t$hash3{$IDPC15}[6]\t$hash3{$IDPC15}[7]\t$hash3{$IDPC15}[8]\t$hash3{$IDPC15}[9]\t$hash3{$IDPC15}[10]\t$hash3{$IDPC15}[11]\t$hash3{$IDPC15}[12]\n";
  112. }
  113.  
  114. close(FILE1);
  115. close(FILE2);
  116. close(OUTPUT);
  117.  
  118. END;
  119.  
Coloreado en 0.006 segundos, usando GeSHi 1.0.8.4


He estado intentando optimizarlo de diferentes maneras, todas sin éxito. Seguramente si usara referencias mejoraría el rendimiento bastante pero estoy aún muy verde con eso. Mi pregunta es la siguiente: ¿veis algún fallo de bulto que ralentice mucho el script?

Un saludo.
Última edición por explorer el 2012-03-15 08:45 @406, editado 1 vez en total
Razón: Formateado de código con Perltidy
Stoner
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2011-03-31 08:41 @403

Publicidad

Re: Rendimiento

Notapor Birei » 2012-03-15 16:13 @717

Me resulta bastante difícil descifrar ese código sin saber qué pretendes conseguir.

Al menos la primera parte se puede acortar evitando llevar y traer los datos de un lado a otro. Aunque me da que el problema de rendimiento está en los bucles anidados.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use strict;
  2. use warnings;
  3.  
  4. open( FILE1,  '<',  'File1' ) or die;
  5. open( FILE2,  '<',  'File2' ) or die;
  6. open( OUTPUT, '>>', 'Output' ) or die;
  7.  
  8. my (%hash1, %hash2, %hash3);
  9.  
  10. while ( my $line1 = <FILE1> ) {
  11.         chomp $line1;
  12.         my @array = ( split /\t/, $line1 )[1..3, 6..9, 11..14];
  13.         $hash1{$PC15} = [@array];
  14. }
  15.  
  16. while ( my $line2 = <FILE2> ) {
  17.         chomp $line2;
  18.         my @array2 = ( split /\t/, $line2 )[0,2,3,5,9];
  19.         $hash2{$lect} = [@array2];
  20. }
  21.  
  22. while ( my ($IDPC15, $valor) = each %hash1 ) {
  23.         ...
  24. }
  25.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Birei
Perlero nuevo
Perlero nuevo
 
Mensajes: 15
Registrado: 2011-06-19 13:27 @602

Re: Rendimiento

Notapor explorer » 2012-03-15 20:13 @884

Esta es mi versión (no probada):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. #use Data::Dumper::Simple;
  5.  
  6.  
  7. open( FILE1,  '<',  'code_30081.txt' );
  8. my %hash1;
  9.  
  10. while ( my $linea = <FILE1> ) {                 # Recorro el primer archivo
  11.  
  12.     #next if $linea =~ /^#/;
  13.     #chomp $linea;
  14.  
  15.     # Campos separados por tabuladores
  16.     #   PC15    PC9     seqPC15 seqPC9  .       .       scaffoldPC15    startPC15       endPC15         sensePC15       .       scaffoldPC9     startPC9        endPC9  sensePC9        .
  17.     #   0       1       2       3       4       5       6               7               8               9               10      11              12              13      14              15
  18.     #
  19.     my @linea           = split /\t/, $linea;
  20.  
  21.     $hash1{ $linea[0] } = [ @linea[1..3, 6..9, 11..14] ];
  22. }
  23.  
  24. close FILE1;
  25.  
  26. #print Dumper %hash1;
  27.  
  28.  
  29. open( FILE2,  '<',  'code_30081_1.txt' );
  30. my %hash2;
  31.  
  32. while ( my $linea = <FILE2> ) {                 # Recorro el segundo archivo
  33.  
  34.     #next if $linea =~ /^#/;
  35.     #chomp $linea;
  36.  
  37.     # Campos separados por tabuladores
  38.     # 0         1       2               3       4       5       6       7       8       9       10      11      12      13      14
  39.     # lect      .       scaffoldlect    poslect .       .       .       .       seqlect .       .       .       .       .
  40.     #
  41.     my @linea           = split /\t/, $linea;
  42.  
  43.     $hash2{ $linea[0] } = [ @linea[2,3,9] ];
  44. }
  45.  
  46. close FILE2;
  47.  
  48. #print Dumper %hash2;
  49.  
  50.  
  51. open( FILE3, '>>', 'Output' );
  52.  
  53. for my $IDPC15 (sort keys %hash1) {
  54.  
  55.     my($scaffoldPC15, $startPC15, $endPC15) = @{ $hash1{$IDPC15} }[3..5];
  56.  
  57.     my $vecesPC15 = 0;
  58.     my $vecesPC9  = 0;
  59.  
  60.     for my $lect (keys %hash2) {
  61.  
  62.         my($scaffoldlect, $poslect, $seqlect) = @{ $hash2{$lect} };
  63.  
  64.         if ($scaffoldPC15  eq  $scaffoldlect
  65.         and $startPC15     <   $poslect
  66.         and $endPC15       >   $poslect + 50
  67.         ) {
  68.             my $esta_en_PC15  =  index($hash1{$IDPC15}[1], $seqlect) > -1;
  69.             my $esta_en_PC9   =  index($hash1{$IDPC15}[2], $seqlect) > -1;
  70.  
  71.             $vecesPC15++  if     $esta_en_PC15  and  not $esta_en_PC9;
  72.             $vecesPC9 ++  if not $esta_en_PC15  and      $esta_en_PC9;
  73.         }
  74.     }
  75.    
  76.     print FILE3 join("\t", $IDPC15, @{ $hash1{$IDPC15} }, $vecesPC15, $vecesPC9), "\n";
  77. }
  78.  
  79. close FILE3;
  80.  
  81. __END__
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Como dice Birei, se pueden reducir bastantes operaciones si no hacemos tantas asignaciones entre variables, y las hacemos directamente a donde queremos guardar la información.

Hablas de usar referencias, y en verdad que sí que las estás usando. Cuando estás usando arrays anónimos, eso ya es usar una referencia a ese array. Y lo que sí podrías hacer es luego usar la notación para acceder a los datos, sin tener que volver a hacer asignaciones.

En cuanto a mi solución, no sé si estará bien, ya que con los dos archivos de prueba que has puesto, no se pueden hacer pruebas (no coinciden los campos como para que se cumplan las condiciones que buscamos).

Unos comentarios al código:
  • en los dos primeros while(), solo nos quedamos con la información que nos interesa. Por ejemplo, en el segundo bucle, descartamos el campo cigar
  • cuando se trata de comparar las líneas de un archivo con los de otro, realmente sobra uno de los bucles, ya que el proceso de lectura de uno de ellos ya es en sí un bucle, por lo que puede ser aprovechado para hacer el proceso, las comparaciones, y el grabado del resultado en el tercer fichero. Si, en este caso, el imperativo es buscar scaffoldPC15 en todas las líneas del segundo archivo, podemos quitar el primer bucle while(), y hacer la lectura de datos del primer archivo en la misma línea 53 (inicio del tercer bucle)
  • en la línea 55 solo extraemos los tres valores que vamos a necesitar en la comparación
  • dentro del bucle más interno, está lo más interesante: solo vamos a cambiar los contadores si se cumplen, primero, las tres condiciones del if() de la línea 64; y segundo, se cumplen las condiciones de no inclusión recíproca (líneas 71 y 72). Aquí hay que notar que la búsqueda la hacemos con index(), ya que es mucho más rápido que usando expresiones regulares, a la hora de buscar cadenas de texto fijas (sin comodines)
  • ya solo queda imprimir la línea hacia el archivo resultado. No es necesario guardar la información en un tercer hash, ya que las claves del %hash1 solo se recorren una vez, por lo que el último bucle que recorría %hash3 es el mismo que recorre %hash1; así que lo quitamos.
Con esto tendrás un buen aumento de velocidad (si no me he equivocado, claro).

Alguna cosa más...
  • dices que el segundo archivo tiene varios Gigas de tamaño. Si estás en Windows o usas un sistema Red Hat muy antiguo, date por perdido... busca otra alternativa. Y asegúrate de que usas un Perl v5.14.2 como mínimo
  • lo que se suele hacer es leer a memoria el archivo más pequeño, y luego recorrer, leyéndolo, el archivo más grande. El tiempo de procesado será al final el mismo, pero estaremos más a salvo de problemas de agotamiento de memoria
  • el sort de la línea 53 es completamente opcional, así que si quieres quitarlo, ganarás un tiempo más. Solo está para conseguir una salida ordenada por IDPC15
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Rendimiento

Notapor Stoner » 2012-03-22 05:05 @254

explorer escribiste:
[*] cuando se trata de comparar las líneas de un archivo con los de otro, realmente sobra uno de los bucles, ya que el proceso de lectura de uno de ellos ya es en sí un bucle, por lo que puede ser aprovechado para hacer el proceso, las comparaciones, y el grabado del resultado en el tercer fichero. Si, en este caso, el imperativo es buscar scaffoldPC15 en todas las líneas del segundo archivo, podemos quitar el primer bucle while(), y hacer la lectura de datos del primer archivo en la misma línea 53 (inicio del tercer bucle)

Alguna cosa más...
  • dices que el segundo archivo tiene varios Gigas de tamaño. Si estás en Windows o usas un sistema Red Hat muy antiguo, date por perdido... busca otra alternativa. Y asegúrate de que usas un Perl v5.14.2 como mínimo
  • lo que se suele hacer es leer a memoria el archivo más pequeño, y luego recorrer, leyéndolo, el archivo más grande. El tiempo de procesado será al final el mismo, pero estaremos más a salvo de problemas de agotamiento de memoria


Tu código funciona perfecto. ¡Muchas gracias, explorer!

Como bien decías, por problemas de memoria lo he cambiado; metiendo en un hash el archivo más pequeño y recorriendo luego el archivo más grande con while(). Así evito tener en memoria un hash tan grande. El problema es que el script es un poquito más lento que el que tu has puesto, ya que creo que recorrer un archivo con while() es más lento que el ir buscando claves en un hash guardado previamente en memoria.

Utilizo Linux y Perl 5.12.4. He intentado instalar 5.14.2 pero no lo consigo, a pesar de poner la carpeta de instalación en mi ruta PATH, perl sigue siendo la versión antigua.

Un saludo y nuevamente gracias por vuestra ayuda.
Stoner
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2011-03-31 08:41 @403

Re: Rendimiento

Notapor explorer » 2012-03-22 05:35 @274

Stoner escribiste:Tu código funciona perfecto. ¡Muchas gracias, explorer!
¿Cuánta ha sido la mejora?

Stoner escribiste:ya que creo que recorrer un archivo con while() es más lento que el ir buscando claves en un hash guardado previamente en memoria.
Sí, así es. Todo lo que sea acceder a disco es más lento que trabajar con la memoria.

Stoner escribiste:Utilizo Linux y Perl 5.12.4. He intentado instalar 5.14.2 pero no lo consigo, a pesar de poner la carpeta de instalación en mi ruta PATH, perl sigue siendo la versión antigua.
perlbrew automatiza todo eso. Ejemplos en App::perlbrew.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Rendimiento

Notapor Stoner » 2012-03-22 12:05 @545

explorer escribiste:¿Cuánta ha sido la mejora?

El script que tu pusiste es del orden de 10 veces más rápido que el mio.
Stoner
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2011-03-31 08:41 @403


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados