>________________________________
> From: JJ Merelo <[email protected]>
>To: Lista de correo de Madrid Perl Mongers <[email protected]>
>Sent: Sunday, May 27, 2012 3:13 PM
>Subject: [Madrid-pm] Optimizando el valor de vuelta de una subrutina
>
>
>Hola
>Pregunta corta: ¿Cómo se hace para que Perl no devuelva un valor de una
>subrutina?
>Pregunta larga: en el MasterMind hay subrutinas que se llaman millones (o
>miles de millones) de veces. El profiler dice que, curiosamente, el devolver
>el valor de la misma es el cuello de botella. Igual hay otra forma de
>solucionarlo, como meterlas inline (la verdad, no sé como hacerlo) pero lo que
>he hecho ha sido declarar el prototipo como que devuelvan void y hacer que se
>les pase el hashref de vuelta como parámetro. Marginalmente mejora algo, pero
>sigue devolviendo el valor devuelto por la última función en la subrutina (un
>map). Añadir return; no mejora prácticamente nada, y declararla como void con
>prototipo tampoco. ¿Alguna idea?
>
No te creas lo que te dice tu profiler!!!
Cuando se activa en Perl el profiling, lo que el interprete hace es llamar a la
subrutina de profiling/debugging (DB::db) cada vez que se va a ejecutar una
nueva sentencia. Normalmente, lo que se hace dentro de esta subrutina, es
cronometrar el tiempo transcurrido entre llamadas sucesivas y atribuir este a
la penúltima operación. Pero a veces, hay operaciones implícitas para las que
no se llama DB::db entremedias y esto es lo que esta pasando en el caso que
planteas. Aqui la operacion oculta es la de abandonar una subrutina (leavesub)
donde se liberan las variables intermedias, se deshacen las local'izaciones, se
limpia el pad, etc.
"return" es la ultima llamada que se ejecuta dentro de una subrutina, y lo
único que hace es meter en la pila uno o varios valores (según el contexto), es
una operación muy barata.
Por ejemplo:
sub foo {
return 84;
}
$a=0;
foo();
$a+=1;
Lo que el profiler ve:
$a=0; # se empieza a ejecutar en t0, consume t1 - t0
return 84; # se empieza a ejecutar en t1, consume t2 - t1
$a+=1; # se empieza a ejecutar en t2, consume t3 - t2
# se acaba en t3
Y con B::Concise se puede ver lo que perl ejecuta realmente:
La parte correspondiente al codigo que no esta dentro de ninguna subrutina:
$ perl -MO=Concise,-exec,-src /tmp/p.pl
1 <0> enter
# 5: $a=1;
2 <;> nextstate(main 2 p.pl:5) v:{
3 <$> const[IV 1] s
4 <#> gvsv[*a] s
5 <2> sassign vKS/2
# 6: foo();
6 <;> nextstate(main 2 p.pl:6) v:{
7 <0> pushmark s
8 <#> gv[*foo] s
9 <1> entersub[t4] vKS/TARG,1
# 7: $a+=1;
a <;> nextstate(main 2 p.pl:7) v:{
b <#> gvsv[*a] s
c <$> const[IV 1] s
d <2> add[t6] vKS/2
e <@> leave[1 ref] vKP/REFC
Y el desemsamblado de foo:
$ perl -MO=Concise,-exec,-src,foo /tmp/p.pl
main::foo:
# 2: return 84;
1 <;> nextstate(main 1 p.pl:2) v
2 <0> pushmark s
3 <$> const[IV 84] s
4 <@> return K
5 <1> leavesub[1 ref] K/REFC,1 <==== Aquí es donde va el tiempo oculto!
Por cierto, las llamadas a DB::db se hacen si no recuerdo mal en cada OP
"nextstate".
Conclusión: lo que estas midiendo es parte de la sobrecarga implícita de llamar
a una subrutina y no hay forma de eliminarla otra que no sea insertar el código
de la subrutina directamente en el punto de llamada (un "inline", vamos), pero
como perl no soporta macros, esto no se puede hacer de manera automatica
(bueno, creo que en las ultimas versiones si con C / XS, utilizando algunas de
las nuevas APIs, pero no parece nada fácil).
_______________________________________________
Madrid-pm mailing list
[email protected]
http://mail.pm.org/mailman/listinfo/madrid-pm