Ensamblador

Algunos tips de cuándo usar ensamblador 

La regla general dice que usamos ensamblador para rutinas que son críticas al tiempo y C cuando es algo más simple.

A mi en lo personal, la mejor forma de optimizar un algoritmo es empezar por hacerlo todo en C y luego tratar de ir optimizando poco a poco.

Hay muchos compiladores muy buenos que generan código bastante compacto. Ganarle al compilador siempre será posible, pero requiere más tiempo. La mayoría de las veces querremos optimizar lo mínimo necesario. Las razones pueden ser varias:

  • No tener tanto código en ensamblador nos permite modificar el algoritmo y probarlo en la PC
  • Porque entre mas tiempo le dediquemos a optimizarlo, nuestro producto será más caro.
  • Porque tenemos una fecha límite de entrega.

En fin, puede haber muchas razones más; pero si el compilador de C soporta mezclar C y ensamblador, ya tenemos la mayor de las ventajas. El primer punto que debemos revisar es cómo mandar parámetros a una función.

Sabemos que en C una función sólo puede regresar un valor. Este valor normalmente se regresa en el acumulador. Pero podemos pasarle muchos argumentos a una función; para esto hay que hacer la tarea, pero las soluciones más comunes (lo decide quien hace el compilador) son:

  • Utilizar el SP y el stack. Esto no quiere decir que los datos se metan a la pila; es probable que los datos se envíen en la parte no usada de la pila.
  • Utilizar registros variados. Con este tipo de solución, puede parecer difícil recordar qué tipo de dato iba en cuál registro, pero a la larga, resulta eficiente. Consiste en acomodar los argumentos según su tipo y tamaño en diferentes registros. Los apuntadores suelen ponerse en registros que pueden direccionar a memoria, los datos de tamaño de palabra grande en acumuladores, etc.
  • Utilizar un Heap / User stack. Aún no me he encontrado un DSP que use algo por el estilo (probablemente esté disponible si usamos un sistema operativo embebido), pero lo menciono porque es una solución que creo muy factible.

Una vez que ya conocemos nuestro procesador y nuestro compilador, podemos optimizar de varias formas.

Contar entradas a funciones

Lo primero que nos conviene hacer es "contar" nuestras funciones: ¿Qué tan grandes son? ¿Qué tanto tiempo tardan en ejecutarse? ¿Qué tan frecuentemente se manda llamar? ¿Contiene muchos ciclos? ¿Contiene muchas bifurcaciones?

Entre mejor podamos contestar estas preguntas, mas fácil es nuestra tarea de optimizar.

Una de las soluciones mas rápidas es agregar una serie de variables globales (no se apuren, las vamos a quitar al final) que se inicialicen en 0 al iniciar el programa y se incrementen cada vez que se llame a  una función (cada función tiene su variable correspondiente). Con esto podemos contar el número de veces que se manda llamar cada función. Eso nos puede dar un buen indicador de qué queremos optimizar de cada función.

Por ejemplo, si una función sólo se manda llamar una vez, podemos buscar la función que la manda llamar y hacer la operación directamente ahí en lugar de saltar a una función.

Para los que tienen una máquina *Nix disponible, el comando time les puede ahorrar mucho tiempo y dar información importante de sus rutinas.

Inline

Antes de entrar con ensamblador, hay algunas cosas que se peden optimizar en C. Una de ellas es usando funciones inline. Con esto logramos que en lugar de que se mande llamar la función (PUSH, CALL, POP), se copie dicha función en aquella que la llama. En ensamblador es lo que llamaríamos una macro.

Una función inline nos va a consumir mas memoria de programa, pero va a ahorrar un poco de procesamiento. Esto es especialmente últil para funciones que se llaman muchas veces y que no son muy largas (como las que se mencionan aquí).


 [ Regresar ]