En la actualidad, las operaciones a nivel de bits, no son demasiado utilizadas. En cambio, hace no demasiados años, se utilizaban con gran frecuencia para optimizar operaciones aritméticas.
No quiero entrar en detalles de demasiado bajo nivel, pero a una CPU, le suele costar menos operar con los bits independientes de un número, que realizar una operación como una multiplicación.
Podemos rotar a la izquierda (<<) para multiplicar por potencias de 2; rotar a la derecha (>>) para dividir entre potencias de dos; utilizar la Y a nivel de bits (&) para averigüar el módulo de potencias de dos, …
Hoy en días, los procesadores son cada vez más rápidos, y con lenguajes interpretados, como PHP, tenía la curiosidad de si seguiría siendo más eficaz.
Por un lado, los nuevos procesadores, suelen ejecutar las operaciones con bits, a la misma velocidad que las operaciones aritméticas simple, aunque también es verdad que estas últimas, se paralelizan peor, por lo que manipular bits directamente sigue ofreciendo algunos beneficios.
Para saber si PHP puede explotar esta ganancia, nada mejor que comprobarlo con el siguiente código:
<?php
set_time_limit(0);
$fInicio=microtime(true);
for ($iCont=0; $iCont<100000; $iCont++)
{
$iTemp=1;
$iTemp=$iTemp<<1; $iTemp=$iTemp<<2;
$iTemp=$iTemp<<3; $iTemp=$iTemp<<4;
$iTemp=$iTemp>>1; $iTemp=$iTemp>>2;
$iTemp=$iTemp>>3; $iTemp=$iTemp>>4;
$iTemp=$iTemp & 1; $iTemp=$iTemp & 3;
$iTemp=$iTemp & 7; $iTemp=$iTemp & 15;
$iTemp=1;
$iTemp=$iTemp<<1; $iTemp=$iTemp<<2;
$iTemp=$iTemp<<3; $iTemp=$iTemp<<4;
$iTemp=$iTemp>>1; $iTemp=$iTemp>>2;
$iTemp=$iTemp>>3; $iTemp=$iTemp>>4;
$iTemp=$iTemp & 1; $iTemp=$iTemp & 3;
$iTemp=$iTemp & 7; $iTemp=$iTemp & 15;
$iTemp=1;
$iTemp=$iTemp<<1; $iTemp=$iTemp<<2;
$iTemp=$iTemp<<3; $iTemp=$iTemp<<4;
$iTemp=$iTemp>>1; $iTemp=$iTemp>>2;
$iTemp=$iTemp>>3; $iTemp=$iTemp>>4;
$iTemp=$iTemp & 1; $iTemp=$iTemp & 3;
$iTemp=$iTemp & 7; $iTemp=$iTemp & 15;
$iTemp=1;
$iTemp=$iTemp<<1; $iTemp=$iTemp<<2;
$iTemp=$iTemp<<3; $iTemp=$iTemp<<4;
$iTemp=$iTemp>>1; $iTemp=$iTemp>>2;
$iTemp=$iTemp>>3; $iTemp=$iTemp>>4;
$iTemp=$iTemp & 1; $iTemp=$iTemp & 3;
$iTemp=$iTemp & 7; $iTemp=$iTemp & 15;
}
$fFin=microtime(true);
echo ($fFin - $fInicio) . '<br>';
$fInicio=microtime(true);
for ($iCont=0; $iCont<100000; $iCont++)
{
$iTemp=1;
$iTemp=$iTemp*2; $iTemp=$iTemp*4;
$iTemp=$iTemp*8; $iTemp=$iTemp*16;
$iTemp=$iTemp/2; $iTemp=$iTemp/4;
$iTemp=$iTemp/8; $iTemp=$iTemp/16;
$iTemp=$iTemp % 2; $iTemp=$iTemp % 4;
$iTemp=$iTemp % 8; $iTemp=$iTemp % 16;
$iTemp=1;
$iTemp=$iTemp*2; $iTemp=$iTemp*4;
$iTemp=$iTemp*8; $iTemp=$iTemp*16;
$iTemp=$iTemp/2; $iTemp=$iTemp/4;
$iTemp=$iTemp/8; $iTemp=$iTemp/16;
$iTemp=$iTemp % 2; $iTemp=$iTemp % 4;
$iTemp=$iTemp % 8; $iTemp=$iTemp % 16;
$iTemp=1;
$iTemp=$iTemp*2; $iTemp=$iTemp*4;
$iTemp=$iTemp*8; $iTemp=$iTemp*16;
$iTemp=$iTemp/2; $iTemp=$iTemp/4;
$iTemp=$iTemp/8; $iTemp=$iTemp/16;
$iTemp=$iTemp % 2; $iTemp=$iTemp % 4;
$iTemp=$iTemp % 8; $iTemp=$iTemp % 16;
$iTemp=1;
$iTemp=$iTemp*2; $iTemp=$iTemp*4;
$iTemp=$iTemp*8; $iTemp=$iTemp*16;
$iTemp=$iTemp/2; $iTemp=$iTemp/4;
$iTemp=$iTemp/8; $iTemp=$iTemp/16;
$iTemp=$iTemp % 2; $iTemp=$iTemp % 4;
$iTemp=$iTemp % 8; $iTemp=$iTemp % 16;
}
$fFin=microtime(true);
echo ($fFin - $fInicio) . '<br>';
?>
En mi servidor de desarrollo, los tiempos obtenidos han sido:
Operador | Tiempo (segundos) | Diferencia (%) |
Operadores de bits | 1,91 segundos | 100,00% |
Operadores aritméticos | 2,06 segundos | 107,85% |
Sorprendentemente, la ganancia, es de casi un 8%, lo cual no está nada mal. Es por tanto una buena idea, reemplazar cálculos aritméticos por cálculos a nivel de bit, siempre y cuando ello sea posible.