General
  Lenguajes
  Graficos
  Juegos
  <Atras>

Optimización de los ciclos

Denomino ciclos en este artículo a la sucesion de enteros que suelen comenzar por cero, aunque no es obligatoro, se van incrementando hasta llegar a un limite y vuelven a empezar por cero. Este tipo de ciclos es lo usual en las animaciones de sprites. Vamos a ver aqui como sencillamente se puede hacer más rápido la actualizacion.

Vamos a verlo todo con un ejemplo un poco estúpido1 pero significativo. Supongamos que la linea 1: es la actualizacion del frame, el limite de frames es de 4 imagenes y estan seran frm = [ 0 | 1 | 2 | 3 ]. La linea 1: parte de que nos salimos del ámbito de las imagenes. En la linea 2: comprobamos si nos fuimos por encima del limite, no comprobamos que se fuera por debajo porque nunca decrementamos solo incrementamos.

En la linea 3: hacemos el modulo entre el limite del valor del frame. Esto nos da cuanto nos pasamos del limite, si nos pasamos dos enteros, tendriamos que haber vuelto al cero y avanzar 2 enteros , frame 2. Esto es lo que hace el módulo.

Se puede ver el código en ensamblador lo costoso que puede ser el módulo.

1:frm=5; 
2:if(frm>=4) 
   
3:  frm=frm%4; 
mov dword ptr [ebp-30h],5
cmp dword ptr [ebp-30h],4
jl  main+129h (00401229)
mov edx,dword ptr [ebp-30h]
and edx,80000003h
jns main+126h (00401226)
dec edx
or  edx,0FCh
inc edx
mov dword ptr [ebp-30h],edx

Vamos a ver que pasa si sustituimos el modulo por el resto. La optimizacion viendo el código ensamblador es evidente. Y podemos hacer esto pero de forma limitada y por la siguiente razon.

Supongamos la recta entera dividida en dos intervalos contiguos de longitud la del limite [0,1,2,3] y [4,5,6,7]. Nosotros queremos estar en el primer intervalo y nos situan en el segundo. Si hacemos la resta por el valor de la longitud del intervalo nos situamos en la misma posición relativa del segundo intervalo en el primer intervalo2.

La limitación de esta versión es que solo funciona si siempre nos movemos por el primer y el segundo intervalo. Podemos asegurar que esto ocurrira si nunca incrementamos mas de la longitud del intervalo.

1:frm=5; 
2:if(frm>=4) 
   
3:   frm=frm-4;
mov dword ptr [ebp-30h],5
cmp dword ptr [ebp-30h],4
jl  main+13Fh (0040123f)
mov eax,dword ptr [ebp-30h]
sub eax,4
mov dword ptr [ebp-30h],eax

El tercer ejemplo, viendo el código en ensamblador, parece peor que el anterior. Pero pongo este ejemplo para los casos en que no se cumpla la restriccion de la anterior. Esto es más rápido que el primer caso ya que no realiza el salto del if. Se da el caso de que los saltos en algunas maquinas son más costosos de lo normal. Esto pasa porque en el salto se vacia el buffer de código y se vuelve a llenar. Por esto es desaconsejado abusar del if else y de otros saltos.

Este caso se puede hacer ya que se da la propiedad en el módulo que aplicado sobre un numero entre (0..limite) da el mismo número.

1:frm=5;
2:frm=frm%4;
mov dword ptr [ebp-30h],5
mov ecx,dword ptr [ebp-30h]
and ecx,80000003h
jns main+156h (00401256)
dec ecx
or  ecx,0FCh
inc ecx
mov dword ptr [ebp-30h],ecx

Se puede ampliar el ejemplo añadiendo un desplazamiento adicional para que no tenga que empezar obligatoriamente en cero el intervalo. Por ejemplo, para un intervalo [despl,despl+1,..,despl+limite] se haria de la siguiente forma:

1:const int superior=despl+limite;
2:frm++; 
3:if(frm>=superior) 
4:   frm=frm-limite;

O lo siguiente:

2:frm++; 
3:frm=(frm-despl)%limite + despl;

Espero que os sirva de algo, un saludo,

Luis Cabellos

1. Bueno muy estúpido

2. Esto es fácil verlo usando una regla y un lapicero para simular la longitud.

3. El código en ensamblador es el generado por Visual C++

Stratos