Bits and Pieces - Scalars (Mordiditas de aquí y de allá)

Que buena idea el poder escribir sobre algo que ya está escrito y bien depurado (como Perl), pero que a su vez está en pleno período de ebullición (como Perl 6) y que además ya tiene una guía como ``Programming Perl''. En resumen : o es muuuuy aburrido (porque está todo dicho) o hay que ser muy brillante para poder encontrar esas cositas que andan por ahí perdidas y puedan adquirir luz propia (que no es mi caso). En este caso simplemente se trata de poder entender mejor lo que hay, tratando de ver las mejores prácticas y elementos más aprovechables de Perl 5 y ayudarnos a entender algunas cosas de Perl 6, o por lo menos para que cuando nos tengamos que meter de lleno no se nos haga la noche de repente.

Y para comenzar que mejor que hacerlo con con el capítulo 2 de ``Programming Perl'' : Bits and Pieces !!!

Los primeros pasos (Atoms, Molecules y Built-in data types) nos ayudan mas bien a comprender ese nivel subyacente en Perl, y son mas parte de la filosofía (qu tratamos en el ``Mordidtas ...'' del mes anterior), y los pasos siguientes (Variables y Names) nos adentran ya un poco más en la parte teórica, pero sin dejarnos una sustancia como para empezar a embebernos y tener algo de práctica. Para mi una de las vedettes de Perl son los scalars, una enings; use strict; use Benchmark qw(:all) ; my $secs = shift || 1; ## segundos de CPU que dura el test my $n = 1000; ## cant. de elementos de cada array ## ## La idea de este script es mostrar la diferencia de performance ## entre un numero puro (IV) y un numero en contexto string (PVIV) ## my $varString = 99; # defino el scalar y almaceno un numero (IV) $varString .= ""; # ... pero ahora lo convierto a string (PVIV) forzandolo # a ser interpretado en un contexto string my $varNum = 99; # scalar sólo numerico (IV) ## ... y después corro el test my @arrayIV; my @arrayPVIV; for my $i (0..($n-1)) { $arrayPVIV[$i] = $varString; $arrayIV[$i] = $varNum; }; my $result = timethese( (-$secs), { IV => sub{ for my $i (0..($n-1)) { $arrayIV[$i]++; }; }, PVIV => sub{ for my $i (0..($n-1)) { $arrayPVIV[$i]++; }; }, } ); cmpthese( $result );

Ejecutando este script con 5 segundos de CPU dio una ventaja de performance entre el 10% y 30% usando el array que posee valores numéricos (IV) versus el que posee valores numéricos pero almacenados también como strings (PVIV). Si ahora hacemos una prueba similar pero haciendo un sort sobre un array de 1000 generados al azar :

 #!/usr/bin/perl
 
 use warnings;
 use strict;
 use Benchmark qw(:all) ;
 
 
 my $secs = shift || 1;
 my $n = 1000; ## elementos del array
 
 ##
 ## La idea de este script es mostrar la diferencia de performance
 ## entre un numero puro (IV) y un numero en contexto string (PVIV)
 ##
 
 my @arrayIV;
 my @arrayPVIV;
 
 getArrayRandom( \@arrayIV, $n );
 for my $i (0..$n-1) {
     $arrayPVIV[$i] = $arrayIV[$i]; ## fuerzo a cada elemento a ser número (IV)
     $arrayPVIV[$i] .= ""; ## y a convertirse en string (PVIV)
 };
 
 ## ... y después corro el test
 
 my $result = timethese( (-$secs), {
     IV => sub{    sort @arrayIV    },
     PVIV => sub{    sort @arrayPVIV;    },
     } );
 
 cmpthese( $result );
 
 sub getArrayRandom($$) {
     my $ref = shift;
     my $items = shift;
 
     for my $i (0..($items-1)) {
         my $len = int( rand(6) ) + 1; ## entero de hasta 5 dígitos
         my $num = int( rand( 1 ) * 10**$len );
         push @$ref, $num;
     };
 };

En este caso la diferencias de performance fueron entre el 30% y 50% a favor del array que posee números convertidos a strings (PVIV) debido a que la comparación default de sort() es string.

Se repite con esto el viejo dicho ``lo que no te mata te fortalece'', porque la ventaja no ser un lenguaje tipado (no poseer tipos de datos como C, C++ o Java) en cierta forma mata a Perl. Pero bien no todo es malo, porque para el próximo release (Perl 6) se está haciendo que puedan definirse las variables como del tipo que se va a usar, lo cual permite evitar este tipo de penalizaciones. Un ejemplo simple en Perl 6 :

 #!/usr/bin/pugs
 
 use v6;
 
 my int $var1 = 10;
 $var1.say();

En principio defino que voy a usar pugs como intérprete (#!/usr/bin/pugs, primer implementación de Perl 6) y que va a ser Perl 6 (use v6). Después defino que hay una variable llamada $var1 que va a ser integer y que es inicializada con el valor 10 (my int $var1 = 10) ... y de paso te recomiendo que si querés empezar a embeberte ya de Perl 6 podés usar Pugs ( http://www.pugcode.org ).

Con esto se reduce el problema de definir y reusar la variable $var1 como un string, pero realmente ``Perl seguirá siendo Perl'' y por lo tanto va a seguir permitiendo que se almacenen strings, con lo cual puedo seguir haciendo :

 $var1 = "Hola";

Entonces, dónde está la ganancia. Claramente, y en un primer punto, en la implementación. En particular si hacemos este manejo Perl 6 se va a comportar distinto que Perl 5, no haciendo cache de este valor, y convirtiendo de integer a string cada vez que lo requiera y almacenando sólo el valor integer.

Lamentablemente aún no está la implementación definitiva de Perl 6 (sobre Parrot, la máquina virtual) con lo cual al momento esto es sólo una promesa (digamos que puede estar implementado en Pugs, pero no me atrevería a tomar como una implementación final con la cual hablar sobre performance).

Infografía