Вы находитесь на странице: 1из 8

ACTIIDAD2 .

Lista de los recursos se consumen en invocacin de


funciones y expresiones simples.

Sobre el rendimiento
1 Prembulo
En algunos sentidos, C++ es un lenguaje de muy bajo nivel (incluso permite insertar instrucciones
ensamblador directamente
4.10), por lo que hay ocasiones en que el programador necesita
habrselas con cuestiones muy de detalle y conocer ntimamente el funcionamiento de los
mecanismos subyacentes. Sobre todo si pretende entender mnimamente el porqu de ciertas
cosas y la terminologa utilizada en los textos de programacin. En este captulo incluimos algunos
conceptos que sin ser estrictamente cuestiones C++ sin duda le ayudarn en su tarea de
habrselas con ese pequeo monstruo.

2 Carga y descarga de funciones


Es importante conocer que C y C++ son lenguajes orientados a pila y estructurados alrededor del
concepto de funcin; el funcionamiento de ambos est ntimamente relacionado. Desde el punto de
vista del programador, la invocacin de una funcin es una sentencia del tipo:
func1();
aunque finalmente aparece como una llamada a la direccin donde se encuentra el recurso
correspondiente. En ensamblador sera algo as:
call 0x4000000

En realidad, lo que ocurre en las tripas de la mquina cuando se invoca una funcin es un proceso
bastante complejo, ya que la invocacin, ejecucin y retorno de una funcin no es solo cuestin de
algoritmo. Tambin hay datos de entrada bajo la forma de los argumentos "pasados" a la funcin
(en base a los cuales el cdigo realizar cierta computacin
4.4.5) y datos de salida en forma
del valor "devuelto".
Nota: observe que si el la llamada a funciones solo interviniese el cdigo, el mecanismo de
invocacin quedara reducido a un salto "jump" al punto de entrada del nuevo bloque de
cdigo.
El asunto es que, aparte de seguir el camino ("path") de ejecucin adecuado, el programa necesita
preparar el entorno de ejecucin (datos) para el nuevo trozo de ejecutable y apuntar l mismo
cierta informacin que le permita volver al punto de partida. Para entender el mecanismo, es
imprescindible desterrar la idea del argumentos "pasados" o valores "devueltos" como datos que
van y vienen desde/hacia la funcin invocante a/desde la funcin invocada.
La anterior es una imagen didctica, diramos de "alto nivel" y adecuada para una explicacin
bsica de los mecanismos de invocacin de funciones. Pero como decamos al principio, C++ es
en ciertos aspectos un lenguaje de bajo nivel (pegado a la mquina) y si se quiere entender y
manejar con xito (en especial si se utilizan programas con mdulos compilados en otros
lenguajes), es fundamental una mirada ms cercana al proceso.

En realidad no existe en absoluto algo como "paso" de argumentos (ni por valor ni por referencia).
Realmente los datos (los argumentos actuales) de la funcin invocante se copian [6] a una zona
de memoria que se crea ex-profeso en la pila (
1.3.2), denominada marco de activacin o
marco de pila ("Stack frame").
Como veremos a continuacin, el marco de pila es un trozo de memoria en este rea, que sirve
como zona temporal de datos para uso de la funcin que entra en ejecucin. En ella se almacenan
los argumentos pasados a la funcin, sus variables locales y otros datos, como direccin de retorno
a la rutina que efectu la llamada, y estado de los registros en el momento de la invocacin.

Por supuesto, toda esta informacin tiene que ser colocada en la pila cada vez que se produce la
llamada a una funcin. El proceso de construir el marco de pila es lo que se denomina secuencia
de llamada. A su vez, cuando termina su ejecucin definitivamente y se devuelve el control a la
funcin que la invoc, la informacin debe ser sacada de la pila [5]. El proceso de desmontar el
marco de pila se conoce como secuencia de retorno. Ambos procesos consumen su tiempo, a
veces bastante.
Las secuencias de llamada y retorno son realizadas por unos trozos especiales de cdigo
denominados prlogo y eplogo que incluye por su cuenta el compilador junto con cada
invocacin a funcin. Aunque en ocasiones esto puede evitarse; son las denominadas funciones
desnudas.
Recuerde que la secuencia de llamada implica la creacin de todas las variables locales de la
funcin (incluyendo los posibles valores que sern devueltos por esta), as como la invocacin del
constructor-copia para todos los argumentos que no han sido pasados por referencia. Por su parte,
en la secuencia de retorno se destruyen todas las variables colocadas en la pila (automticas)
invocando los destructores correspondientes y sus valores se pierden. Recordemos tambin que
los valores estticos que hubiese en la funcin tienen espacio de almacenamiento independiente y
pueden conservarse.
Conviene resaltar que en el proceso de invocacin de una funcin solo intervienen dos actores: la
funcin que realiza la invocacin ("Caller") y la funcin que es invocada ("Called"); nosotros las
denominamos funcin invocante y funcin invocada (o llamada). Entrambas tienen que repartirse el
trabajo de las secuencias de llamada y retorno.
Normalmente en C++ es la funcin invocante la encargada de limpiar la pila y de colocar all los
parmetros. Esto es precisamente lo que hace posible el uso de funciones con nmero variable de
parmetros, ya que en tiempo de compilacin, la funcin invocada no sabe cuantos argumentos
recibir.

3 El marco de pila
La figura 1 muestra la forma de ocupacin de la pila cuando es invocada una nueva funcin y se
crea el correspondiente marco de activacin. Observe que la pila crece "hacia abajo", es decir,
desde posiciones altas de memoria hacia posiciones ms bajas.
La funcin invocante ("Caller") ocupa una primera zona con copia de los argumentos pasados a la
funcin invocada. Generalmente esta copia se realiza empezando por el ltimo y terminando por el
primero (derecha-izquierda). Por ejemplo, en la invocacin:
func(a, b, c);
Los argumentos seran pasados en el orden c,b, a. En la figura su colocacin sera de abajo a
arriba a partir del punto comienzo del nuevo registro de activacin.
Nota: el paso de argumentos incluye naturalmente su evaluacin previa (recuerde que la
sintaxis C/C++ permite utilizar argumentos que son el resultado de una expresin). En lo que
respecta al orden en que son pasados, y evaluados, los argumentos, aunque la convencin
derecha-izquierda es la usual en plataformas Intel, puede variar en otras, por lo que en orden a
la portabilidad, y como regla de buena prctica, NO se deben hacerse suposiciones relativas al
orden en que sern evaluados los argumentos en las funciones C/C++, y mucho menos,
utilizar argumentos cuyo valor dependa de este orden.
A continuacin se incluye informacin sobre el estado de la mquina (valores de los registros). Esta
informacin ser utilizada ms tarde en el proceso de restaurar la ejecucin de la funcin
invocante, de forma que la ejecucin siga en la instruccin siguiente a la invocacin.
A continuacin se sita un valor denominado enlace dinmico o de control. Se trata de un puntero
que seala al enlace dinmico del registro de activacin anterior, que a su vez seala al precedente
(as para toda la secuencia de funciones invocadas en un momento dado). Evidentemente la
funcin anterior es la invocante, y en caso de llamadas recursivas (cuando la funcin se invoca a s
misma), es la activacin previa de la misma funcin. Cuando se inspecciona el estado de la pila

con un depurador, la primera funcin que se encuentra es main; sobre ella, la cadena de
funciones invocadas que en ese momento no han tenido retorno.
La direccin del enlace dinmico est contenida en un registro del procesador denominado puntero
base BP ("Base Pointer"
H.3.2). Esta direccin es importante porque en el proceso de enlazado
desaparecen todos los nombres de variables. En el fichero objeto los nombres son sustituidos por
las direcciones de almacenamiento correspondientes, y en el caso de las variables locales
dinmicas de las funciones, incluyendo las copias de los argumentos, estas posiciones estn
expresadas como desplazamientos en bytes ("Offset") a partir de esta posicin (de ah el nombre
de puntero "base").
Por ejemplo: para traer un valor cuyo desplazamiento es 8 de la pila al acumulador (registro AX), al
procesador le basta una sola instruccin que en ensamblador puede tener el siguiente aspecto:
...
mov
...

ax,[bp+8]

A partir de la posicin del enlace dinmico se sita un rea, rellenada por la funcin invocada
("Called"), que contiene todas las variables locales dinmicas de la nueva funcin (las variables
estticas disponen de su propio espacio de almacenamiento ( 1.3.2). A continuacin viene un
rea denominada de variables temporales que contiene datos auxiliares del compilador.

4 Sustitucin inline
Ocurre con frecuencia, sobre todo en la invocacin a funciones pequeas, que el costo de las
secuencias de llamada y retorno suponen mucho ms que el costo de memoria necesario para el
cuerpo de la propia funcin que se invoca. De hecho, C++ dispone de un especificador de tipo de
almacenamiento (que solo es aplicable en la definicin de funciones), especialmente concebido
para atender este problema. Se trata de la directiva inline (palabra-clave
3.2.1).
Durante la fase de enlazado (
1.4), en cada punto del cdigo donde aparece la invocacin a una
funcin, se coloca una direccin que seala la situacin del recurso correspondiente (el cdigo
compilado de la misma), pero mediante la directiva inline, se indica al compilador que en vez del
comportamiento habitual, sustituya esta direccin por una copia del cdigo de la funcin, lo que se
denomina expansin inline. Resulta evidente que de esta forma se eliminan las secuencias de
llamada y retorno, lo que se traduce en una ejecucin mucho ms rpida. La contrapartida es que
el tamao del ejecutable resultante es mayor, ya que existe ms de una copia de la funcin; tantas
como sustituciones inline se hayan efectuado. Adems, el artificio presenta algunos inconvenientes
que sern comentados a continuacin.

La declaracin de una funcin como sustituible


tiene la forma general:

inline, se realiza en el sitio de su definicin, y

inline <tipo_dev> <funcin> (<parmetros>) {<sentencias>;}

En cualquier sitio donde el cdigo encuentre una invocacin a <funcin>, el compilador sustituir
la invocacin por el cdigo contenido en <sentencias>, incluyendo la creacin de las variables
locales pertinentes. Por ejemplo [2]:
inline float mod (float x, float y) { return sqrt(x*x + y*y); }
inline char* cat_func(void) { return char*; }
Nota: algunos compiladores exigen que la definicin
invocacin a la funcin.

inline se realice antes que cualquier

En ocasiones, el compilador puede hacer caso omiso de la indicacin inline; se trata de la misma
situacin que con las peticiones register (
4.1.8b), es decir, un mandato no imperativo para el
compilador.
En cambio, otras veces el compilador supone una sustitucin inline aunque no se
indique explcitamente. Es el caso de las denominadas funciones inline, mtodos cuya
declaracin y definicin se realizan dentro del cuerpo de la clase (
4.11.2a), o el de
determinadas invocaciones a funciones muy pequeas incluidas en el cuerpo de otras. En estos
casos, el mecanismo de optimizacin del compilador pueden decidir que su cdigo sea incluido en
el cuerpo de la funcin invocante [1].
En cualquier caso, las correspondientes directivas de compilacin, suelen permitir al programador
bastante control al respecto de este tipo de sustituciones, incluyendo posturas intermedias y
extremas. Por ejemplo, con objeto de facilitar la depuracin del programa, es posible indicar al
compilador que provisionalmente no realice este tipo de sustituciones
. En otros casos, se le
puede ordenar que utilice su criterio para establecer que funciones pequeas son merecedoras de
la sustitucin inline [3]. Finalmente, cabe la opcin de dejar la sustitucin exclusivamente a criterio
del programador [4].

5 Casos especiales
5.1 Las funciones con especificador de excepcin (
sustitucin inline.

1.6.4), no son susceptibles de

5.2 Evidentemente, estas funciones no son susceptibles de recursin (invocarse a s mismas) por
lo que generalmente el compilador ignora la directiva inline en estos casos.
Nota: los compiladores de Microsoft permiten la substitucin inline si la profundidad de
recursin puede ser deducida en tiempo de compilacin, y siempre que esta profundidad no
sobrepase un lmite previamente especificado por el programador.

532 Dependiendo de su estructura interna, algunas funciones no son susceptibles de este tipo de
sustitucin. Por ejemplo, en el compilador Borland C++ 5.5 no pueden ser sustituidas inline las
funciones que contengan alguna sentencia de iteracin while; do... while y for (
4.10.3).
Algunos compiladores rehsan efectuar la substitucin si el cuerpo de la funcin es muy grande, y
tampoco realizan la sustitucin en algunos casos en que la invocacin de la funcin se realiza
mediante punteros.

5.4 Las funciones que acepten algn parmetro que sea del tipo "clase con un destructor", no
pueden ser objeto de expansininline. Sin embargo, esta restriccin no es aplicable si se trata de
un paso por referencia. En el primer caso el compilador lanza un mensaje de aviso anunciando que
la directiva inline no ser tenida en cuenta.
Ejemplo:
struct est {
...
est();
// Constructor por defecto
~est();
// Destructor
};
inline void f1(est& e) { /* ... */ }
inline void f2(est e) { /* ... */ }
La definicin de f1 compilar sin problema, ya que el parmetro es una clase con destructor, pero
pasa por referencia. En la compilacin de f2 se producir un mensaje de aviso: Functions
taking class-by-value argument(s) are not expanded inline in function
f2(est).

5.5 Cualquier funcin que devuelva una clase con destructor no puede ser objeto de
expansin inline, cuando dentro de la expresin de retorno puedan existir variables u objetos
temporales que necesiten ser destruidos.
Ejemplos:
struct est {
est();
// constructor por defecto
~est();
// destructor
};
inline est f1() {
// Ok: puede ser sustituida inline
return est();
}
inline est f2() {
// Aviso: No sustituible inline
est e2;
return est();
}
Esta funcin no puede ser sustituida, porque el objeto e2 necesita ser destruido, en consecuencia,
se genera un aviso del compilador: Functions containing some return statements are
not expanded inline in function f2().
En esta otra:
inline est f3() {
// Aviso: No sustituible inline
return ( est(), est() );
}
se genera un mensaje de aviso anlogo al anterior. Tampoco puede ser sustituida porque el valor
devuelto contiene objetos temporales.

6 Criterio de uso
En aras a la velocidad de ejecucin, es preferible evitar en lo posible la utilizacin de funciones
pequeas, especialmente en bucles que se repiten un gran nmero de veces. En caso de tener
que utilizarlas es preferible acudir a la sustitucin inline. Tambin son buenas candidatas a esta
sustitucin las funciones-operador (
4.9.18).
Aunque lo anterior supone ir contra dos reglas generales de la buena programacin:
la reutilizacin del cdigo, y lacompartimentacin de datos y procedimientos. En este
sentido, la sustitucin inline supone una situacin intermedia; sin las desventajas de la llamada y
retorno a funcin, pero (desde el punto de vista del programador), con las ventajas de la utilizacin
de funciones en cuanto suponen el encapsulamiento del cdigo en un nico sitio. La opcin a elegir
en cada caso (funcin tradicional o sustitucin inline), es como siempre una cuestin definir
prioridades entre el cronmetro y el tamao del cdigo resultante.
Nota: a menos que el compilador permita otro tipo de medida al respecto
, cuando sea
importante reducir el tamao del cdigo, debe recordar definir las funciones miembro fuera del
cuerpo de la definicin de la clase, para evitar la sustitucin inline antes aludida, que en estos
casos es realizada automticamente por el compilador.

Cuando se trata de optimizar funciones que no son escritas por el programador. Por ejemplo,
cuando se utilizan los recursos de la Librera Estndar, es conveniente recordar que los modernos
compiladores traen algunas de estas libreras en dos formatos: comofunciones y como macros, y
el programador puede optar entre una y otra forma 8
.

7 Depuracin de funciones inline


Puesto que en estas sustituciones el compilador reemplaza la llamada a funcin por "su versin"
del cdigo de la misma, an cuando en la compilacin se hayan incluido las opciones de
depuracin (
1.4), no existe una correspondencia entre las lneas de cdigo del fuente y el
ejecutable, lo que hace difcil la depuracin de este tipo de funciones. Para evitar estas dificultades,
los compiladores ofrecen una serie de opciones con las que se pueden controlar diversos aspectos
de la construccin del ejecutable en las versiones de depuracin.
Nota: para facilitar la depuracin de estas funciones, algunos compiladores ignoran la
directiva inline cuando se compila en modo "debug".
En el caso concreto del compilador BC++ se ofrece el siguiente abanico de posibilidades de
compilacin (
1.4.3):

-v
-v-vi
-vi-

Opciones de depuracin ON; expansin inline OFF


Opciones de depuracin OFF; expansin inline ON
Expansin inline ON
Expansin inline OFF (las funciones inline son expendidas fuera de lnea)

El compilador GNU Cpp ofrece la opcin -fno-default-inline, que hace que las funciones
miembro no sean consideradas inlinepor el mero hecho de haber sido declaradas dentro del
cuerpo de la clase.

8 Macro-sustitucin
La macro-sustitucin, a la que hemos hecho referencia en el prrafo anterior, es una tcnica
similar a la sustitucin inline que ha sido ampliamente utilizada (como herencia del C), pero que
no debe ser confundida con esta ltima. Su utilizacin ha cado bastante en desuso y est
desaconsejado, dado que presenta algunos inconvenientes que se detallan en el captulo
correspondiente (#define
4.9.10b). Se basa en la utilizacin del preprocesador C/C++ para
simular la invocacin de funciones que no son tales. Ejemplo:
#define abs (x) (x < 0? (-x) > x)
...
func f(x) {
int y = abs(x);
...
}
Es frecuente que algunas rutinas de librera, que adoptan la forma de funciones pequeas, puedan
venir en formas dos formatos: como funcin y como macro, pudiendo optar el programador entre
una y otra forma. Esta ltima (las macros) viene a ser el equivalente de la sustitucin inline para
tales funciones de librera. Ver "Funciones y macros" (
5.1) para una clarificacin sobre esta
cuestin.

ACTIVIDAD4 los criterios de tiempo de ejecucin o extensin de cdigo


generado

EXTENSIO -Las transformaciones deben preservar el


N
significado de los programas
-Una transformacin debe acelerar los
programas en una cantidad mensurable
- Una transformacin debe valer la pena

CRITERIOS DE
UN
GENERADOR

-Tiempo de ejecucin (optimizacin temporal)


-Espacio de memoria utilizado (optimizacin
espacial)
Generacin de cdigo: Mientras se analiza el
EJECUCCION
programa y Libre del contexto.
Optimizacin de cdigo: Despus de la generacin
de cdigo de todo el programa o de un elemento
ejecutable de este (funcin, procedimiento, etc.)
Dependiente del contexto