\newpage
%===========================================================================
%       x.3 > Paralelismo
%===========================================================================
\pagestyle{empty}
\begin{latexonly}
\ifpdf
 \begin{figure}[htbp]
         \includegraphics[bb = -250 150 0 0, scale=1]{./TuXes/tux-counting.jpg}
 \end{figure}
 \vspace{5in}
\else
 \begin{figure}
           \includegraphics[-50,580][0,0]{./TuXes/tux-joint}
 \end{figure}
\fi
\hfill\line(1,0){400}
\end{latexonly}
\topmargin 1in
\section{PARALELISMO}
\begin{latexonly}
\line(1,0){400}
\newpage
\end{latexonly}
\topmargin 0in
\pagestyle{fancy}
%========================cita===============================================
\begin{latexonly}
\hspace{1in}\textcolor{blue_omosix}{{\em Software is like sex: it's
better when it's free.}}

\hspace{4in}\textcolor{blue_omosix}{{\em Linus Torvalds}}
\vspace{0.5in}

\end{latexonly}
%===========================================================================
En esta secci\'on se tratar\'a el palalelismo a nivel te\'orico.
 Como se explica en la secci\'on de introducci\'on, paralelizar los problemas
 en problemas m\'as sencillos y distribuirlos entre varios procesadores no
 siempre es una soluci\'on conveniente y depender\'a en gran medida de la naturaleza de
 los problemas que se quieran tratar.

Se hablar\'a de programaci\'on paralela y los problemas que pueden surgir cuando se trata
 de utilizar este paradigma.
 Para ello debe considerarse el sistema sobre el que se programa y se lanzan
 los programas paralelos: ya sean multiprocesadores o, como ser\'a el
 caso, multicomputadores.
 Al explicar estos l\'\i mites que impone la propia arquitectura
 tendr\'a que hablarse forzosamente de la {\bf ley de
 Ahmdal} as\'\i \ como de otros problemas relativos a la ordenaci\'on de instrucciones
 debido a dependencias.
 
En general en esta secci\'on se hablar\'a acerca de todos los conceptos que interesan
 a la hora de programar aplicaciones y hacerlo utilizando de una manera
 u otra el paralelismo.
 Se dar\'an diversos ejemplos de programaci\'on con openMosix y explicaciones con
 PVM o MPI y se profundizar\'a en conceptos como la {\bf granularidad del paralelismo}
 o el {\bf problema de la transparencia} en lo que se refiere a la
 programaci\'on.
%===========================================================================
\subsection{Definiciones previas}
\subsubsection{Par\'ametros de rendimiento en computaci\'on paralela}
{\bf Velocidad de ejecuci\'on {\em rate (R)}.} Mide {\em salidas (outputs)} por unidad de
tiempo. Seg\'un la naturaleza de las salidas, tendremos:
\begin{itemize}
  \item $R= \frac{instrucciones}{segundo}$ que se miden con
  MIPS (instrucciones x $10^6$ por segundo).
  \item $R= \frac{op's}{segundo}$ que se miden con
  MOPS (operaciones x $10^6$ por segundo).
  \item $R= \frac{op's flotantes}{segundo}$ que se miden con
  MFLOPS (op's coma flotante x $10^6$ por segundo).
\end{itemize}

\vspace{.2in}
{\bf Acceleraci\'on ({\em speedup} $S_p$).} Ratio entre el tiempo de una
ejecuci\'on serie y una paralela.
 \begin{equation}
\displaystyle S_p=\frac{t_1}{t_p}
\label{eq_speedup}
 \end{equation}

\begin{itemize}
  \item[]$t_1$: tiempo requerido para realizar la computaci\'on en $1$ procesador,
  \item[]$t_p$: tiempo requerido para realizar la computaci\'on en $p$ procesadores.
  \item[]Normalmente se da la relaci\'on $1 \leq S_p \leq p$, debido al tiempo
  que se pierde en la inicializaci\'on, sincronizaci\'on, comunicaci\'on y
  otros factores de {\em overhead} que se requieren en la computaci\'on paralela.
\end{itemize}

\vspace{.2in}
{\bf Eficiencia (E).} Ratio entre la acceleraci\'on de una ejecuci\'on
paralela y el n\'umero de procesadores.
 \begin{equation}
\displaystyle E=\frac{S_p}{p}=\frac{t_1}{p\ t_p}
\label{eq_eficiencia}
 \end{equation}

\vspace{.2in}
{\bf Redundancia (R).} Ratio entre el n\'umero de operaciones realizadas
utilizando $p$ procesadores en una ejecuci\'opn paralela y su correspondiente
ejecuci\'on serie en 1 procesador.
 \begin{equation}
\displaystyle R=\frac{O_p}{O_1}
\label{eq_redundancia}
 \end{equation}

\vspace{.2in}
{\bf Utilizaci\'on (U).} Ratio entre $O_p$ y el n\'umero de operaciones que
podrian realizarse utilizando $p$ procesadores en $t_p$ unidades de tiempo.
 \begin{equation}
\displaystyle U=\frac{O_p}{p\ t_p}
\label{eq_utilizacion}
 \end{equation}
%---------------------------------------------------------------------------
\subsubsection{Modelos matem\'aticos}
{\small\bf\tt I. }{\bf\sc Principio Arm\'onico Ponderado}

\vspace{.2in}
Sea $t_n$ el tiempo requerido en computar $n$ operaciones de $k$ tipos
diferentes, donde cada tipo de operaci\'on consiste en $n_i$ operaciones
simples las cuales requieren $t_i$ segundos cada una para su
ejecuci\'on. Luego:

$\displaystyle t_n=\sum_1^k n_i t_i $ \ \ \ \ y\ \ \ \ $n=\sum_1^k n_i$. Por definici\'on
sabemos que $R_n=\frac{n}{t_n}=\frac{n}{\sum_1^k n_i t_i}$ MFlops.

\vspace{.2in}
Si definimos:
\begin{itemize}
 \item[] $f_i=\frac{n_i}{n}$ como fracci\'on de operaciones ejecutadas a una
 velocidad $R_i$,
 \item[] $t_i=\frac{1}{R_i}$ velocidad de ejecuci\'on en computar 1
 operaci\'on,
\end{itemize}

luego

 \begin{equation}
\displaystyle R_n=\frac{1}{\sum_1^k f_i t_i}=\frac{1}{\sum_1^k\frac{f_i}{R_i}}
 \end{equation}

donde $\sum_i f_i =1, R_i=\frac{1}{t_i}$.

\vspace{.2in}
Lo importante de esta ecuaci\'on es que muestra que una sola operaci\'on
donde su ejecuci\'on no sea equiparable con el resto, en cuanto al tiempo de ejecuci\'on, tiene una
gran influencia en el rendimiento global del sistema.

\vspace{.3in}
{\small\bf\tt II. }{\bf\sc Ley de Amdahl}
 \begin{equation}
\displaystyle R(f)=\frac{1}{\frac{f}{R_H}+ \frac{1-f}{R_L}}
\label{eq_amdahl}
 \end{equation}

Para comprender esta ley ser\'\i a conveniente recuperar la analog\'\i  a del problema que se planteaba en el primer tema,
 acerca de la persona que deb\'\i  a colocar los libros en dos partes. 
En el caso de la ordenaci\'on de la biblioteca se llegaba a la conclusi\'on de
 que era est\'upido contratar a 200 personas para que organizasen una biblioteca
 de 200 ejemplares, por la falta de eficiencia en las operaciones.

El {\em speedup} ~--o mejora de rendimiento~-- idealmente aumenta linealmente con
 el n\'umero de unidades de procesamiento que posea el sistema.
 Esto es en un principio cierto, pero estamos contando en todo momento
 con un programa modelo ideal, sin tener en cuenta la naturaleza de dicho programa.
 En el caso de programas concretos hemos de contar con la naturaleza del
 programa, qu\'e pretende solucionar y la manera en la que lo soluciona.
 Eso permitir\'a poder dar con la medida real del {\em speedup}.

 En cualquier programa paralelizado existen dos tipos de c\'odigo: el c\'odigo paralelizado y el c\'odigo secuencial.
 Como es sabido existen ciertas secciones de c\'odigo que ya sea por dependencias,
 por acceso a recursos \'unicos o por requerimientos del problema
 no pueden ser paralelizadas. Estas secciones conforman el c\'odigo secuencial, que
 debe ser ejecutado por un solo elemento procesador.
 Es pues l\'ogico afirmar que la mejora de rendimiento de un programa
 depender\'a completamente de:
\begin{enumerate}
\item El tiempo en el que se ejecuta el c\'odigo serie $R_L$.
 \item El tiempo en el que se ejecuta el c\'odigo paralelizable $R_H$.
\item El n\'umero $f$ de operaciones ejecutadas de forma paralela (en tiempo $R_H$).
\end{enumerate}

\vspace{.2in}
Esta es la llamada {\bf ley de Amdahl} y fue descrita por Gene Amdahl
 en 1967.
 Las implicaciones que trae esta ecuaci\'on son, a pesar de que no
 tenga en cuenta las caracter\'\i sticas de cada sistema en concreto:
\begin{itemize}
  \item el rendimiento no depende completamente del n\'umero
 de procesadores que posea el sistema: en la mayor\'\i a de los casos
 depender\'a del n\'umero de procesadores m\'aximo que se aprovechar\'an simult\'aneamente
 para ejecutar un programa.
  \item cuanto mejor paralelizado est\'e un programa m\'as susceptible ser\'a
 de aumentar su {\em speedup} y por tanto explotar el rendimiento del sistema paralelo que lo ejecute.
\end{itemize}

Supongamos ahora que tenemos un programa que inicialmente no hemos paralelizado,
 cuyos tiempos de ejecuci\'on son 12\% y 88\%, en serie y en paralelo respectivamente.
\begin{latexonly}
\ifpdf
        \begin{figure}[htbp]
        \includegraphics[bb = -250 250 0 0, scale=.6]{./pics/amdahl_speedup.png}
        \vspace{2in}
        \caption{Paralelismo. Ejemplo de incremento de speedup obtenido con la ley de Amdahl}
        \end{figure}
\else
\end{latexonly}
        \begin{figure}
        \centering
        \includegraphics{pics/amdahl_speedup}
        \caption{Paralelismo. Ejemplo de incremento de {\em speedup} obtenido con la ley de Amdahl}
        \end{figure}
\begin{latexonly}
\fi
\end{latexonly}

Como se puede ver en la figura, la parte no paralelizable del c\'odigo impide
 que se pueda escalar de forma lineal, llegar\'a un momento que a\~{n}adir nuevos
 procesadores no a\~{n}adir\'a una ventaja real al sistema, porque todo lo que
 estar\'a en ejecuci\'on ser\'a c\'odigo secuencial.
 Por lo tanto para maximizar el aprovechamiento de los sistemas paralelos
 debe tenerse mucho cuidado con la forma de paralelizar las aplicaciones:
 cuanto m\'as c\'odigo secuencial tengan, m\'as problemas de escalabilidad.

\vspace{.2in}
La ley de Amdahl es un caso particular del Principio Arm\'onico Ponderado,
anteriormente descrito. Si recuperamos:
\vspace{.2in}

$\displaystyle R_n=\frac{1}{\sum_1^k \frac{f_i}{R_i}}$ \hspace{.3in} y hacemos $K=2$ queda \hspace{.3in}
$\displaystyle R_2=\frac{1}{\sum_1^2 \frac{f_i}{R_i}}$

\vspace{.2in}
si se igualan
\begin{itemize}
  \item[] $f_1=f$: fracci\'on paralela
  \item[] $f_2=(1-f)$: fracci\'on serie
\end{itemize}

da la forma de la Ec.(\ref{eq_amdahl})\hspace{.2in}$\rightarrow$\hspace{.2in}$\displaystyle R(f)=\frac{1}{\frac{f}{R_H}+
  \frac{1-f}{R_L}}$ .

\vspace{.3in}
{\small\bf\tt III. }{\bf\sc L\'\i mites de la computaci\'on paralela}

\vspace{.2in}
Un programa paralelo requiere sincronizar las tareas de que se
compone. Estas tareas se distribuyen entre los diferentes procesadores del
sistema paralelo. La suma de los tiempo de ejecuci\'on de estas tareas en un
sistema paralelo es m\'as grande que la suma de los tiempos de ejecuci\'on en
un solo proesador. Esto es debido a diferentes tiempos adicionales de
procesamiento ({\em overhead}).

Sean:
\begin{itemize}
  \item[] $t_s$ : tiempo de sincronizaci\'on
  \item[] $t$ : granularidad de la tarea. valor medio del tiempo de
  ejecuci\'on de las diferentes tareas.
  \item[] $t_0$ : {\em overhead} de la tarea debido a su ejecuci\'on en paralelo.
  \item[] $N$ : n\'umero de tareas entre puntos de sincronizaci\'on.
  \item[] $p$ : n\'umero de procesadores.
\end{itemize}

El tiempo requerido para ejecutar $N$ tareas, cadauna con un tiempo de
ejecuci\'on $t$ en un solo procesador viene dado por
\begin{center}
 $\displaystyle t_1=N\ t$
\end{center}

\vspace{.2in}
En un entorno paralelo cada tarea requiere $t+t_0$ unidades de tiempo para su
ejecuci\'on. El tiempo requerido para ejecutar $N$ tareas en $p$ procesadores
ser\'a pues:
\begin{center}
 $\displaystyle t_{N,p}=t_s+ \left\lceil \frac{N}{p}\right\rceil (t+t_0)$
\end{center}

y recuperando el {\em speedup}, esta vez siendo el c\'omputo las $N$ tareas

 \begin{equation}
\displaystyle S_{N,p}=\frac{T_{1}}{T_{N,p}}=\frac{N\ t}{t_s + \lceil \frac{N}{P}\rceil (t+t_{0})}
\label{eq_snp}
 \end{equation}

y la {\em eficiencia} ahora quedaria determinada como

 \begin{equation}
\displaystyle E_{N,p}=\frac{S_{N,p}}{p}
\label{eq_eflim}
 \end{equation}

Los l\'\i mites de la computaci\'on paralela estan reflejados en el cuadro \ref{cuad_Paralelismo01}.

\begin{table}
\begin{center}
\begin{tabular}[b]{ | c | c | c | } \hline
\rule{0cm}{.15in}
{\bf Par\'ametro}  & {\bf $p \rightarrow \infty$ , $N$ fijo} & {\bf $N
  \rightarrow \infty$ , $p$ fijo} \\ \hline

\rule{0cm}{.3in}
$\displaystyle S_{N,p}$ & $\displaystyle \frac{N}{1+\frac{t_s+t_0}{t}}$ &
  $\displaystyle \frac{p}{1+\frac{t_0}{t}}$ \\ \hline

\rule{0cm}{.3in}
$\displaystyle E_{N,p}$ & 0 & $\displaystyle \frac{1}{1+\frac{t_0}{t}}$ \\
\hline
\end{tabular}
\caption{Paralelismo. L\'\i mites de la computaci\'on paralela}
\label{cuad_Paralelismo01}
\end{center}
\end{table}

\vspace{.2in}

%===========================================================================
\subsection{L\'\i mites en el hardware}
En lo que se refiere a explotaci\'on de programas paralelos en m\'aquinas SMP
 hay que tener en cuenta el modo de ejecuci\'on
 de las instrucciones. Actualmente la mayor\'\i a de los ordenadores comerciales son de los llamados
 {\em ordenadores superescalares}: lanzan varias instrucciones a la vez aprovechando una divisi\'on por fases.
Esta divisi\'on se construye sobre el principio que cada fase requiere de una parte espec\'\i fica del hardware del procesador para completarse. De esta manera se aprovechan mejor todo los componentes del procesador pues se utilizan a cada ciclo.

Para lograrlo deben solucionar de la manera m\'as apropiada posible retardos
 por dependencias\footnote{Cualquiera de los tres tipos de
 dependencia que existen: dependencia real, dependencia inversa y
 antidependencia. Otra terminolog\'\i a utilizada es dominio-rango,
 rango-dominio y rango-rango.}. Generalmente esto se soluciona con renombramiento de registros (fase que
 puede o no hacer el compilador), lanzamiento de instrucciones fuera de
 orden, unidades superescalares y supervectoriales y algunas otras t\'ecnicas que
 se describir\'an sin ahondar en el cap\'\i tulo dedicado a arquitecturas.

La granularidad del hardware es la instrucci\'on m\'aquina (o en lenguaje ensamblador). Esta unidad es la m\'as fina que puede tratar.
Existen gran variedad de mejoras de rendimiento a
 los l\'\i mites que se imponen por el programa, estas mejoras se van incorporando
 en los procesadores de \'ultima generaci\'on.
 El problema es que esta soluci\'on puede resultar en muchos casos demasiado
 cara, sobretodo por la dificultad o tiempos de implementaci\'on.
 Adem\'as siempre se necesita cierta ayuda de parte del software, principalmente
 el compilador.
 As\'\i\  aunque el Pentium4 tenga nuevas instrucciones vectoriales
 SSEII, no son pr\'acticamente usadas por los compiladores actuales y el
 rendimiento de un P4 es bastante inferior al esperado.
 En cambio usando el compilador especialmente desarrollado por Intel, los
 mismos programas aprovechan las instrucciones vectoriales del procesador
 mucho mejor con lo que se consiguen programas mucho m\'as r\'apidos.

 Hoy en d\'\i a es dif\'\i cil ver programar en ensamblador para procesadores que
 no est\'en dedicados a tareas como dispositivos empotrados o de tiempo real,
 e incluso en estos este lenguaje se hace m\'as raro cada dia.
 Por lo tanto las optimizaciones de uso de las nuevas capacidades de los
 procesadores tienen que venir de parte del compilador que es quien se encarga
 de trabajar a nivel m\'as bajo que el programador.
%===========================================================================
\subsection{L\'\i mites del software}
Como se ha visto, la ley de Amdahl pone l\'\i mites en lo que se refiere al
 incremento del rendimiento de cualquier sistema en el que se utilicen fragmentos de c\'odigo no paralelizable, es decir, de todos los sistemas que actualmente se conocen.
 Una vez asumida dicha limitaci\'on, no queda m\'as opci\'on que optimizar los programas
 para que el rendimiento de los mismos en el sistema
 sea el mejor posible. Esto amenudo implica a\~{n}adir una fase m\'as en la vida
 de nuestros programas.

 As\'\i\ pues a las fases de an\'alisis, dise\~{n}o e implementaci\'on hay que a\~{n}adir
 una fase de paralelizaci\'on, en la que se deber\'a tener en cuenta las necesidades
 del programa y del problema a resolver, as\'\i\ como de los medios de los que
 se disponen para poder hacer que el programa aumente el rendimiento a medida
 que aumenta la capacidad de procesamiento de sistema.
 
Existen otras muchas limitaciones adem\'as de la ley de Amdahl que afectan
 al desarrollo de aplicaciones paralelas, comenzando por el sistema elegido
 para paralelizar aplicaciones, hasta la granularidad del paralelismo (que
 a este nivel siempre ser\'a denominado paralelismo de grano grueso).
 Muchos de estos m\'etodos son brevemente referenciados en el tema de sistemas
 distribuidos, otros son de una granularidad algo m\'as fina, relativos a
 compiladores que prepar\'an el c\'odigo paralelizado para sistemas especiales.

Para ilustrar esto con un caso pueden suponerse dos compiladores diferentes para un mismo lenguaje. 
Un compilador {\em inteligente} frente a uno que no lo
 es tanto ante una misma entrada: un programa con una secuencia de
 divisiones con coma flotante, despu\'es unas sumas del mismo tipo y por \'ultimo
 unas operaciones con enteros, todas sin dependencias entre
 s\'\i\. Ocurre que:
\begin{itemize}
  \item El compilador {\em relajado} no las reordena con lo que si el procesador no tiene
 ordenaci\'on de instrucciones har\'a que las instrucciones de enteros, que seguramente tardan solamente 1 o 2, ciclos tengan que esperar a las instrucciones de divisi\'on
 en coma flotante, operaciones que pueden tardar m\'as de 30 ciclos.
 Mientras se hacen las divisiones en coma flotante no se est\'an usando las
 unidades de suma de flotantes ni las de operaciones con enteros. Estos componentes del procesador permanecen ociosos mientras otros se usan intensivamente.
  \item En cambio el compilador {\em inteligente} primero podr\'\i a ejecutar las instrucciones que m\'as tiempo
 tardar\'an en ejecutarse, y despu\'es ejecutar\'\i a las instrucciones que usan otras
 unidades para que el procesador pueda estar procesando de forma paralela
 todos los datos, aprovechando al m\'aximo su hardware.
 Por supuesto que esta misma operaci\'on (primero las operaciones que m\'as
 tardan) si se realiza sobre las mismas unidades hace que el retardo medio
 de finalizar una instrucci\'on aumente por lo que el compilador debe hacer
 las elecciones correctas si est\'a intentando realizar una
 ordenaci\'on \'optima.
\end{itemize}
%===========================================================================
\subsection{Granularidad del paralelismo}
Al hablar en los apartados anteriores acerca de la granularidad del paralelismo
 se hac\'\i a referencia a la implicaci\'on que tiene el paralelismo en el \'ambito de
 la programaci\'on.
 De este modo, se puede decir que la granularidad en el paralelismo de un
 sistema se encuentra en paralelizar c\'odigo de procesos, rutinas, m\'odulos
 o bucles a nivel de instrucci\'on.
 El t\'ermino {\bf granularidad se usa como el m\'\i nimo componente del sistema que
 puede ser preparado para ejecutarse de manera paralela}.
 Por norma general cuanto m\'as fuertemente acoplado (en el sentido descrito
 en la secci\'on Arquitecturas) es un sistema, menor es la granularidad del
 paralelismo en la programaci\'on.
 Dependiendo del grado de granularidad del sistema se diferencia en:
\begin{enumerate}
 \item Sistemas de granularidad fina.
    \begin{enumerate}
      \item bucles
      \item sentencias 
    \end{enumerate}
  En general se hace a nivel de instrucciones en ensamblador.
  Generalmente son explotados por sistemas hardware muy caros con los nodos
  o procesadores fuertemente acoplados.
  Es la granularidad m\'as peque\~{n}a y basa pr\'acticamente todo su funcionamiento
  en propiedades del hardware.
  El hardware puede ser suficientemente inteligente para que el programador
  no tenga que hacer mucho por soportar esta granularidad, por ejemplo el
  hardware puede aportar reordenamiento de instrucciones.
 
 \item Sistemas de granularidad media.
    \begin{enumerate}
       \item m\'odulos
       \item rutinas
       \item tareas o procesos
    \end{enumerate}
 Dentro de estos sistemas se incluyen varios de los que se describir\'an m\'as adelante,
 como pueden ser RPC, openMosix y otros, si bien estos mismos est\'an entre el
 paralelismo de grano grueso y el paralelismo de grano medio. El paralelismo de grano medio en general es explotado por el programador
 o el compilador.\\
 Dentro de \'el tambi\'en se encuentran diversas librer\'\i as como pueden ser
 PVM o MPI.
 El hardware normalmente tambi\'en se prepara para poder aprovechar este tipo
 de paralelismo, por ejemplo, los procesadores pueden disponer de instrucciones
 especiales para ayudar en el cambio de una tarea a otra que realiza el
 sistema operativo.

 \item Sistemas de granularidad gruesa.
   \begin{enumerate}
     \item trabajos o programas
     \item m\'odulos o procesos
   \end{enumerate}
 El paralelismo de grano grueso es el que explota el programador mediante
 programas que no tienen por qu\'e requerir la utilizaci\'on de ninguna librer\'\i a
 externa, sino solamente el uso de conocimientos de programaci\'on para paralelizar un algoritmo.
 Se basa principalmente en cualquier tipo de medio que utilice el programador
 para crear un programa, que solucione un problema de manera paralela, sin
 tener por que hacer uso m\'as que de su habilidad de programador y de un
 buen algoritmo.
 Son los m\'as limitados al carecer de m\'etodos espec\'\i ficos para comunicaci\'on
 entre nodos o procesadores, se dan en sistemas muy d\'ebilmente acoplados.
\end{enumerate}

Una vez vistos los tipos de granularidades existentes podemos catalogar
 algunos de los sistemas que vamos a tratar a lo largo de este documento.
 Para empezar cabe se\~{n}alar que mayormente se tratar\'a del estudio de sistemas
 distribuidos y clusters, esto implica que el sistema est\'a compuesto por
 diversas m\'aquinas con un sistema operativo que puede hacer el sistema m\'as
 acoplado o no.
 El software de cluster en el que se particulariza es openMosix, como se explicar\'a
 con mucho m\'as detalle. La granularidad de openMosix est\'a en los procesos, es
 decir, podemos paralelizar procesos y hacer que estos se ejecuten en nodos
 distintos.
 De hecho openMosix se encarga de balancear
 la carga de los nodos de modo que la carga global del sistema permanezca
 homog\'eneamente distribuida.
 Este tipo de granularidad es del tipo medio, si bien puede ser confundida
 con la de grano grueso (el l\'\i mite siempre esta bastante difuso).
 Otros sistemas como pueden ser RPC, MPI, PVM o clusters Beowulf se acercan m\'as al paralelismo de grano grueso.
%===========================================================================
\subsection{El problema de la transparencia}
Uno de los mayores problemas que existen en la creaci\'on de programas que
 hagan uso de paralelizaci\'on (quitando los de granularidad fina que
 ya se ha visto que son explotados a bajo nivel por el compilador y por el hardware)
 es la transparencia en la programaci\'on de dichos programas.
 A la hora de crear un programa que resuelva un problema mediante el uso
 de un algoritmo que explote de alguna manera la paralelizaci\'on hay que
conocer el sistema de ejecuci\'on.
 Dependiendo del sistema elegido y teniendo en cuenta que por norma general
 se paralelizan tareas, procesos, procedimientos, rutinas o componentes
 distribuidos que realizan este trabajo, hay dos modelos de implementaci\'on:
\begin{enumerate}
  \item Modelo de programaci\'on expl\'\i cita.

 En el que se requiere una biblioteca de funciones especiales que se
 encargan tanto de realizar la comunicaci\'on como los m\'etodos de migraci\'on y dem\'as factores
 que en un principio\footnote{Seg\'un el concepto de dise\~{n}o de esa biblioteca.} no
 debe afectar al programador, el cual debe abstraerse de esta capa.
 Este tipo de modelo es el que utiliza RPC, MPI o PVM.
 Requiere un conocimiento especial de dichas bibliotecas, lo que limita
 la velocidad de desarrollo del software y adem\'as lo hace m\'as costoso debido
 al tiempo que se debe gastar en el conocimiento de las funciones.

  \item Modelo de programaci\'on impl\'\i cita.

 Es un modelo quiz\'a m\'as atractivo. Basa todo su funcionamiento
 en que el programador sepa lo m\'\i nimo del sistema para paralelizar sus procesos.
 Generalmente este modelo lo explotan los compiladores especiales de un
 sistema particular.

 Por otro lado se suele depender de macros o funciones
 especiales que delimitan la granularidad de los procesos a migrar.
 Un caso especial de este tipo es openMosix: la programaci\'on y migraci\'on de
 procesos en openMosix no requiere de conocimiento del usuario respecto al
 cluster de m\'aquinas.
 Lo ideal para obtener transparencia en la programaci\'on ser\'a programar de
 manera completamente impl\'\i cita y que al mismo tiempo el sistema implantado
 fuese lo menos intrusivo posible en lo que se refiere a comportamiento
 con el usuario final.
\end{enumerate}
Como se puede ver en ambos sistemas y en el estado de arte en el que se encuentran
 los sistemas distribuidos y clusters, no existe de momento ninguna
 manera, ya sea mediante programaci\'on expl\'\i cita o impl\'\i cita, de hacer uso
 de la transparencia en la migraci\'on de procesos, o la ejecuci\'on de procesos
 remotos, el uso de tareas programadas con PVM o MPI, {\em etc.}

 Para que un sistema fuese realmente transparente y al mismo tiempo suficientemente eficiente, en
 lo que se refiere a programaci\'on paralela, el programador deber\'\i a
 conocer a fondo la programaci\'on del sistema, i.e. las habituales
 llamadas de creaci\'on de memoria, librer\'\i as est\'andar, o basadas en las est\'andar,
 llamadas a sistema y dem\'as herramientas habituales de la programaci\'on.
 Por otro lado, el compilador o el sistema se deber\'\i an encargar de efectuar
 todas aquellas tareas que de manera habitual se hacen en los sistemas que
 actualmente existen, es decir, la comunicaci\'on entre procesos, la localizaci\'on
 de dichos procesos, rutinas o tareas, la paralelizaci\'on del c\'odigo del
 programa y dem\'as tareas.
 As\'\i\  el programador no tendr\'\i a que preocuparse de los detalles y se podr\'\i an
 hacer desarrollos m\'as r\'apidos y baratos.

Lamentablemente dicho sistema no existe.
 La explotaci\'on de los sistemas distribuidos de momento requiere un gran
 conocimiento del sistema en el que se trabaja por parte del programador.
 De hecho, las optimizaciones de los algoritmos se basan en gran medida
 no en c\'omo de bueno es el modelo del algoritmo, sino en las capacidades
 especiales de cada sistema.

Es decir, cada sistema especial como puede ser Beowulf, PVM u openMosix tienen
 puntos fuertes y puntos d\'ebiles en lo que se refiere a la programaci\'on,
 y es mediante el conocimiento de estas caracter\'\i sticas de la manera que
 se pueden optimizar programas paralelos.
 Un ejemplo de esto puede encontrarse en aplicaciones que requieren
 comunicaci\'on entre procesos. El caso de openMosix, como se ver\'a m\'as detalladamente,
 es el caso de uno de los sistemas que poco a poco ha pretendido ser
 un sistema lo mas transparente posible y que basa toda su granularidad
 del paralelismo en el {\em proceso}.
 Las aplicaciones dise\~{n}adas para ejecutarse en openMosix contienen procesos
 que se ejecutan de manera independiente y que de una manera u otra se deben
 comunicar de manera transparente con otros procesos.
 Estos procesos pueden estar en el nodo local donde se ejecutaron o en otros
 nodos, lo cual complica la cosa, ya que la relaci\'on que une a un proceso
 con otro puede ser un {\em pipe} con nombre y sin embargo estar los procesos
 en dos nodos distintos.

 Imag\'\i nese el t\'\i pico caso de la multiplicaci\'on de una matriz.
Se supone que el caso ideal para openMosix ser\'\i a utilizar memoria
 compartida para las matrices y dejar que el sistema haga todo de la manera
 m\'as efectiva posible.
 Sin embargo openMosix no implementa en las versiones actuales\footnote{Y no hay espectativas que lo consiga, por la propia funcionalidad que persigue.} un manejo eficiente
 de la memoria compartida y por tanto se deben buscar
 otros mecanismos de programaci\'on para aprovechar al m\'aximo la utilizaci\'on del sistema.
 

Otro posible ejemplo ser\'\i a el de PVM.
 Sup\'ongase el caso de determinadas tareas con uso de CPU intensiva que se
 sit\'uan en unos nodos concretos antes de empezar su ejecuci\'on.
 Sup\'ongase tambi\'en que el tiempo de ejecuci\'on de dichas tareas es en muchos
 casos aleatorio, y por tanto, el sistema tendr\'a muchos nodos altamente
 cargados y otros poco cargados. Como PVM no posee de la capacidad de
 balanceo autom\'atico de carga que tiene openMosix se estar\'a desaprovechando la capacidad
 de c\'omputo del cluster en general y la tarea que se le encomend\'o tendr\'a
 que esperar hasta que el \'ultimo de los nodos acabe con ella, mientras los
 dem\'as nodos est\'an ociosos y no pueden ayudar en su finalizaci\'on.
 En cualquier caso la transparencia en los sistemas distribuidos y clusters que m\'as se utilizan actualmente requiere de un conocimiento
 exhaustivo por parte del programador, lo que en muchos casos requiere un
 coste adicional\footnote{Si bien es cierto que los dos ejemplos que hemos mencionado anteriormente
 representan el escenario ideal para utilizar ambos clusters, openMosix y PVM
 de manera conjunta, de modo que uno se encarge de las transferencias de
 las matrices y la memoria compartida y el otro se encargue de el balanceo
 de la carga de las tareas que PVM lanza.}.

Por otro lado est\'a el problema de la intrusi\'on en el c\'odigo.
 En este caso se supondr\'a el caso ideal de haber conseguido
 un sistema realmente transparente a los ojos del programador, lo que implicar\'\i a
 que la mayor\'\i a de las aplicaciones necesitar\'\i an de una compilaci\'on especial
 o un compilador especial para el sistema en concreto, lo que har\'a las
 aplicaciones normales incompatibles con nuestro sistema, teniendo que compilar
 todas las aplicaciones de nuevo.
 En un principio se podr\'\i a pensar que en el caso de el sistema operativo
 linux esto no ser\'\i a problema m\'as que para las distribuciones (aparte de
 alg\'un programa de m\'as bajo nivel que habr\'\i a que rehacer).
 
El problema real est\'a en lo intrusivo que puede llegar a ser un sistema
 de estas caracter\'\i sticas, cuando los servicios distribuidos que se proveer\'\i an
 solamente ser\'\i an apreciados por una peque\~{n}a parte de los usuarios del sistema
 operativo, seguramente por esto se crear\'\i a m\'as antipat\'\i a que simpat\'\i a a
 estos cambios en el kernel.

 Uno de estos casos ha sido el de Amnon Barak.
 Amnon Barak es el l\'\i der del proyecto Mosix.
 En una de las primeras implementaciones trat\'o de hacer un sistema como el
 que se ha descrito, con el resultado de tener que implementar
 aproximadamente el 60\% del kernel, m\'as la reimplementaci\'on de
 las librer\'\i as necesarias para ejecutar los programas m\'as
 habituales; esto sin tener en cuenta que todos
 los programas que eran afectados por dichas librer\'\i as deb\'\i an de ser recompilados para poder ser utilizados.
De esta manera se puede hacer notar que por el momento el tener un
 sistema transparente en lo que se refiere a la programaci\'on supone tener
 un sistema altamente intrusivo.
%===========================================================================
\subsection{Paralelizaci\'on de programas}
Se ha visto que la ley de Amdahl limita el incremento
 de rendimiento de los programas cuando \'estos se ejecutan en sistemas
 multiprocesadores.
 Tambi\'en que la optimizaci\'on de los programas suele depender en
 la mayor\'\i a de los casos del conocimiento del sistema m\'as que del algoritmo
 que se utilice.
 Comprendiendo esto puede empezarse a definir c\'omo debe ser el software a
 nivel de aplicaci\'on para explotar al m\'aximo el paralelismo de estos
 sistemas.
 Por un lado se intentar\'a, bas\'andonos en la ley de Amdahl, que el aumento
 de rendimiento (o {\em speedup}) de los programas sea el m\'aximo posible, esto
 implica que los fragmentos de c\'odigo no paralelizable deben ser los m\'\i nimos
 posibles.
 Por otro lado deber\'an conocerse las limitaciones y caracter\'\i sticas del sistema
 para el que programaremos.

El primer paso en el desarrollo de un programa paralelizado es, como siempre, plantear el problema
 mediante t\'ecnicas de divide y vencer\'as.
 Deber\'a localizarse lo paralelizable en el problema de manera
 abstracta antes de pensar en la paralelizaci\'on del c\'odigo, es decir
 que existen problemas inherentemente paralelos, como pueden ser la suma
 de las componentes de dos vectores seg\'un sus posiciones o la
 renderizaci\'on de im\'agenes. Estos problemas se pueden resolver de manera \'optima.
 Por otro lado, est\'an los problemas en los que en un principio no vemos
 claramente la inherencia del paralelismo como pueden ser la multiplicaci\'on
 de matrices, compresi\'on de la informaci\'on o compilaci\'on.

 Te\'oricamente se puede paralelizar cualquier cosa que se haya diseminado
 mediante t\'ecnicas de divide y vencer\'as, procesos, m\'odulos rutinas, o algoritmos
 paralelos completamente.

Existen dos formas bien conocidas y f\'aciles de comprender de paralelismo:
\begin{enumerate} 
 \item El {\bf paralelismo funcional} divide las aplicaciones en funciones.
 Se podr\'\i a ver como paralelismo de c\'odigo.
 Por ejemplo puede dividirse en: entrada, preparaci\'on del problema, soluci\'on
 del problema, preparaci\'on de la salida, salida y mostrar la soluci\'on.
 Esto permite a todos los nodos producir una cadena.
 Esta aproximaci\'on es como la segmentaci\'on en funciones de un
 procesador.

 Aqu\'\i\ se sigue la misma idea pero a nivel de software,
 se dividen los procesos formando una cadena y dependiendo uno del siguiente.
 Aunque al principio no se logre el paralelismo, una vez que se ponen todos
 los nodos a trabajar (i.e. cuando hayamos ejecutado {\tt N} veces lo
 que estemos ejecutando, siendo {\tt N} el n\'umero de pasos en los que hemos dividido la aplicaci\'on) se consiguen que todos los nodos est\'en trabajando a la vez si todas las
 funciones tardan el mismo tiempo en completarse.
 Sino, las dem\'as funciones tienen que esperar a que se complete la funci\'on
 m\'as lenta.

 La idea es exactamente la misma que trataremos en el tema {\em Arquitecturas}
 aunque all\'\i\ se referencie con el t\'ermino {\em pipeline}.
 Esta forma de paralelizar no es ampliamente usada puesto que es muy dif\'\i cil
 dividir en funciones que tarden el mismo tiempo en ejecutarse.
 S\'olo se usa en casos concretos donde se ve claramente que es una buena
 opci\'on por ser f\'acilmente implementable.

 \item El {\bf paralelismo de datos} se basa en dividir los datos que se tienen que procesar.
 T\'\i picamente los procesos que est\'an usando esos datos son id\'enticos entre
 s\'\i\  y lo \'unico que hacen es dividir la cantidad de informaci\'on entre los
 nodos y procesarla en paralelo.
 Esta t\'ecnica es m\'as usada debido a que es m\'as sencillo realizar
 el paralelismo.

 Ejemplos pueden ser los films {\em Final Fantasy} o {\em El se\~{n}or de
 los anillos} que fueron renderizadas en un cluster de renderizaci\'on con
 el software Maya y Renderman.
 Esta forma de renderizar se basa en dividir los frames (cuadros) que se
 deban renderizar en una escena en concreto o todos los frames de la pel\'\i cula
 entre todos los ordenadores.
 Para ser m\'as exactos, primero se divide el n\'umero de frames por el n\'umero
 de nodos y se env\'\i a el n\'umero resultante de frames a cada nodo (suponiendo
 que es un cluster donde todos los nodos tienen la misma capacidad de proceso).
 Esta es la fase de preparaci\'on y suele ser secuencial, puesto que es una
 peque\~{n}a parte de la producci\'on.
 Una vez hecho esto, cada nodo renderiza los frames que le fueron asignados.
 Esta fase puede durar semanas.
 Una vez se tienen todos los frames se unen en un fichero \'unico que tiene
 toda la pel\'\i cula.
 En este caso en particular se usa paralelismo de datos, y aunque algunas
 im\'agenes tardan m\'as en renderizarse que otras y por lo tanto alg\'un nodo
 tardar\'a menos en acabar su trabajo que otro, lo que se hace es no elegir
 im\'agenes de forma secuencial (de la 1 a la {\tt n} al nodo 1) sino paralela (1
 al nodo 1, 2 al nodo 2...
 n+1 al nodo n+1) esto suele compensar la carga entre los nodos por lo que
 m\'as o menos todos los nodos se mantienen en el m\'aximo rendimiento (si no
 se comprende esto, piense en lo poco que tardar\'\i a el nodo que renderize
 unos t\'\i tulos de cr\'edito simples).
\end{enumerate}
Por supuesto muchos problemas se pueden solucionar con cualquiera de los
 dos paradigmas.
 En el anterior ejemplo podr\'\i a haberse hecho una divisi\'on funcional y
 hacer que un nodo renderizase luces, otro pusiera texturas, otro reflejos,
 {\em etc.} En cambio otras soluciones son espec\'\i ficas o al menos mucho m\'as efectivas
 con una de las dos aproximaciones.
 Todos los casos en los que no se tiene una idea clara de lo que pueda tardarse
 en una funci\'on es m\'as paralelizable con paralelismo de datos.
 
Una vez analizada la fuente de paralelismo de los programas s\'olo falta
 comenzar a realizar el estudio en pseudoc\'odigo o cualquier otro sistema
 parecido.
 Hay que conocer los l\'\i mites y caracter\'\i sticas del
 sistema para el que va a programarse.
 La migraci\'on o ubicaci\'on de un proceso, m\'odulo o rutina est\'a ligada a un
 coste adicional; hemos de conocer los costes de nuestros sistemas y evaluar
 la conveniencia de paralelizar acciones.

 Un programa paralelizado no debe obtener peor tasa de eficiencia que un
 programa secuencial, lo cual no siempre se cumple ni es posible debido
 a transferencias de comunicaci\'on entre los procesos, m\'odulos, nodos o elementos
 del cluster.
 En este apartado juega un papel importante la red de comunicaci\'on, el
 cual ocupa un cap\'\i tulo entero.
 La paralelizaci\'on del c\'odigo est\'a comprometida a las ventajas de los sistemas
 implantados, por tanto no podremos hablar en forma general de \'esta.
 En cualquier caso se dar\'a una visi\'on de un sistema concreto
 openMosix y se har\'a alguna alusi\'on a sistemas del tipo PVM.

La paralelizaci\'on del software se basa en principios generales asociados
 a la manera de programar que utilizamos actualmente tanto por arquitectura
 de los computadores, funcionamiento de compiladores como lenguajes de programac
i\'on.
 Uno de estos principios generales es el denominado principio de localidad,
 del cual existen dos tipos de localidades en nuestros programas: 
\begin{itemize}
  \item Localizaci\'on temporal.

Este tipo de localizaci\'on se basa en la manera en la que se puede encontrar
 el c\'odigo de los programas y como se ejecutan estos en el tiempo.
 Generalmente, los programas est\'an compuestos por un alto porcentaje de
 estructuras en forma de bucle que ejecutan instrucciones.
 Lo que implica que con tener en memoria estos bucles, tendremos acceso
 a la mayor parte del programa.
 Por otro lado normalmente procedimientos o funciones que est\'an relacionadas
 suelen estar en zonas de memoria adyacentes, as\'\i\ como las instrucciones
 que se ejecutan secuencialmente.

  \item Localizaci\'on espacial.

Este tipo de localizaci\'on se basa en la manera en la que los programadores
 organizamos nuestros datos en estructuras, objetos, matrices, vectores.
 Esta manera de organizar elementos de los programas que est\'an muy relacionados
 entre s\'\i\ hace que a la hora de solicitar memoria para los mismos \'estos
 sean alojados en segmentos contiguos de memoria.
 Cuando en un programa se pide memoria, se intenta pedir de una vez toda
 la memoria que vayan a usar la mayor parte de datos posible, para precisamente
 tener que estar pidiendo memoria continuamente.
 Esto produce esta localidad.
\end{itemize}

Nuestra intenci\'on principal deber\'\i a ser explotar el paralelismo al m\'aximo nivel,
 para esto pueden utilizarse las t\'\i picas formas que se usan para organizar
 la informaci\'on y paralelizar al m\'aximo posible, accesos a la misma, operaciones
 a realizar y dem\'as acciones.
 Por ejemplo, un programa cuyo n\'ucleo de proceso principal se encuentre
 en un bucle ser\'\i a perfectamente paralelizable, pongamos que el bucle se
 realizara 15000 veces. En el caso de tener 3 nodos podemos distribuir su
 ejecuci\'on en 3 procedimientos que se ejecuten en estos tres nodos y que
 realicen bucles de 5000 iteraciones de bucle cada uno.
 Para que esto sea posible es necesario cumplir un objetivo que tambi\'en
 debemos tener en cuenta siempre que programemos aplicaciones paralelas
 en sistemas distribuidos como clusters.
 Los segmentos de c\'odigo, rutinas, bucles o cualquier operaci\'on sobre estructura
s de datos, deben ser ortogonales, en el sentido de que no deben interferir entre s\'\i .

 Al paralelizar programas se ve que se obtiene como ventaja un incremento
 del rendimiento. Este incremento puede ser decremento si estamos comunicando
 continuamente nuestros procesos a trav\'es de una red, que generalmente suele
 ser el cuello de botella del sistema.
 Es por esto que los elementos de procesos a paralelizar deben ser lo m\'as
 independientes y ortogonales posibles.

Tambi\'en hay que pensar que si un proceso tiene que dedicar instrucciones
 a la comunicaci\'on con otros procesos, este c\'odigo no es paralelizable y
 adem\'as es c\'odigo que a\~{n}adimos a cada uno de los procesos, por lo que la
 escalabilidad disminuye dr\'asticamente con la cantidad de informaci\'on pasada
 entre procesos.
 Aqu\'\i\ es cuando es especialmente importante el desarrollo software pues
 una buena divisi\'on en procesos puede evitar esta sobrecarga de comunicaci\'on.

En el caso que aqu\'\i\ se trata, programaci\'on de clusters o sistemas distribuidos,
 se llamar\'an {\em elementos de proceso} a los m\'odulos, procesos, procedimientos
 o rutinas que se consolidan como unidades de proceso a ejecutar en los
 nodos, aunque haya que diferenciarlos claramente
 del concepto que tiene el t\'ermino elemento de proceso en sistemas multiprocesad
or.

 Los elementos de proceso deben ser ortogonales entre s\'\i .
 El problema que anula generalmente la ortogonalidad de estos
 elementos, esto es la dependencia de datos.
 Cuando surgen dependencias, ya sea de datos o de control\footnote{Entendiendo por
 dependencia de control el momento en el que un elemento debe tomar el control
 de un recurso \'unico no compatible, y por tanto solicitar permiso al resto
 o por lo menos avisarlos para evitar situaciones de interbloqueo}, el programa o sistema deja
 de ser paralelizable y pasa a ser secuencial, se necesita interconectar
 generalmente los elementos para obtener sus resultados o para obtener sus
 estados y poder continuar el programa, esto requiere como ya hemos dicho
 comunicaci\'on a trav\'es de la red, lo que puede llegar a ser bastante costoso.

No existe un m\'etodo general para evitar las dependencias o para ortogonalizar
 elementos de proceso, es por esta raz\'on por la que todav\'\i a puede ser denominado arte en
 lugar de ciencia a este tipo de investigaciones.
 En la mayor\'\i a de ocasiones se debe utilizar algoritmos que en programaci\'on
 normal se dar\'\i an por descartados a simple vista.
 En otras ocasiones el sistema permite utilizar algoritmos \'optimos paralelizables.
 De este modo, aunque el sistema sea el mejor sistema paralelo existente, si la resoluci\'on de los problemas que tienen que hacer no son paralelizables estaremos tirando tiempo y dinero.
 La mayor\'\i a del esfuerzo en el desarrollo de programas que resuelvan problemas
 de manera paralela, se gasta en:

\begin{itemize}
  \item Estudio completo del problema.

En el cual se eval\'ue las maneras de atacar el problema, se
dividan las distintas secciones de \'este y se tenga en cuenta
 las implicaciones.
 Se corresponde con la parte de an\'alisis del programa

  \item Estudio de la paralelizaci\'on del problema.

En la cual se busca la ortogonalizaci\'on del problema para que este pueda
 ser f\'acilmente paralelizable.
 Se eval\'uan todas las posibles v\'\i as de paralelizaci\'on y se eligen las que
 m\'as se adecuen al sistema concreto sobre el que se vaya a ejecutar.

  \item Estudio del sistema.

Tanto a nivel pr\'actico como te\'orico.
 Por ejemplo en el caso de PVM basta con conocer el modelo de programaci\'on,
 su sintaxis y el cluster sobre el que se va a ejecutar, la carga que puede
 aceptar cada nodo, dispositivos y recursos de cada nodo, {\em etc.}\\
 En el caso de openMosix se debe conocer el estado del cluster, las maneras
 actuales de poder comunicar procesos en el sistema, creaci\'on de entorno
 adecuado.
 Cada sistema debe ser explotado de una manera diferente.

\item Estudio de la paralelizaci\'on del c\'odigo.

Este apartado depende completamente de los dos anteriores, es decir, s\'olo
 se puede hacer correctamente en el caso que se hayan cumplido los dos apartados
 anteriores, corresponde con la fase de dise\~{n}o del programa.
 Es en esta parte del desarrollo en la que muchas veces se deben cambiar
 algoritmos adecuados por otros que lo son menos para que sean m\'as f\'acilmente
 paralelizables en un sistema concreto y se obtenga mejor rendimiento del
 mismo.

\item Pruebas del sistema.

 Muchos programas desarrollados mediante el paradigma de programaci\'on paralela
 hacen uso de un modelo de programaci\'on poco ortodoxa, que los hace dif\'\i ciles
 de probar y de depurar al mismo tiempo.
 Por ejemplo, cuando se ejecuta el {\tt gdb} sobre un programa que lanza otros
 programas y se comunica con estos mismos, la depuraci\'on se hace un tanto
 m\'as dif\'\i cil.
 Otra consecuencia de la programaci\'on poco ortodoxa es que en muchas ocasiones
 el programa no se comporta de la manera o con la eficiencia con la que
 hab\'\i amos supuesto contar\'\i a en un principio\footnote{Muchas veces esto suele deberse a falta de conocimiento del sistema sobre el que se ejecuta el programa en cuesti\'on.}.
\end{itemize}
%===========================================================================
\subsection{Ejemplos de problemas paralelizables}
Generalmente los programas convencionales no cuentan con ninguna manera de paralelizar
 su c\'odigo. Es decir, ser\'\i a genial poder contar con alg\'un ejemplo
 de programa que comprima de formato DVD a DivX en paralelo y poder comprimir
 una pel\'\i cula en un cluster como openMosix, o poder ejecutar programas de compresi\'on
 en varios nodos a la vez.
 La realidad es otra.
 La mayor\'\i a de los programas son pensados y implementados de manera secuencial.
 Si bien existen muchos campos donde el desarrollo de programas que hagan
 uso de una manera u otra de paralelismo, est\'a creciendo cada vez m\'as.
 Actualmente, entre estos campos contamos con:
\begin{itemize}
 \item granjas de compilaci\'on 

 \item granjas de renderizaci\'on

 \item procesos matem\'aticos 
    \begin{itemize}
    \item multiplicaci\'on de matrices
    \item suma de vectores por componentes
    \item multiplicaci\'on de matrices o vectores por escalares o funciones escalares.
    \item integrales definidas (creando intervalos m\'as peque\~{n}os como elementos de proceso)
    \end{itemize}

 \item compresi\'on

 \item aplicaci\'on a nuevos paradigmas inherentemente paralelos 
    \begin{itemize}
    \item redes neuronales
    \item algoritmos gen\'eticos
    \end{itemize}

\end{itemize}

En el campo cient\'\i fico es en el que m\'as se est\'an haciendo desarrollos de
 este tipo de programas ya que es el campo en el que generalmente es m\'as
 f\'acil encontrarse problemas a paralelizar por la naturaleza de estos.
 Actualmente otras nuevas v\'\i as de desarrollo est\'an siendo bastante utilizadas.

Existen programas de renderizado como MAYA o 3D Studio que utilizan paralelismo
 en forma de {\em threads} para poder hacer uso de sistemas multiprocesador, de
 modo que el tiempo de renderizado se reduzca considerablemente.
 Es una l\'astima que ninguno de estos sistemas haga uso de soluciones libres
 que permitan abaratar el coste del sistema, por ejemplo un MAYA para Linux
 (que existe) que utilice un cluster Beowulf, PVM, MPI u openMosix permitiendo
 abaratar gastos a la empresa que lo utiliza y al mismo tiempo, reducir tiempos
 de renderizado.
 Para aprovechar varios nodos se usa un programa especial llamado Renderman
 el cual decide a que nodo enviar las im\'agenes a ser renderizadas.
 Este programa es una soluci\'on particular que se desarroll\'o para
 {\em Toy Story} (cluster de m\'aquinas SUN) y que s\'olo funciona para el problema de las granjas
 de renderizaci\'on.

Existen tambi\'en m\'ultiples entornos de trabajo distribuidos en empresas que
 utilizan de una manera muy superficial paralelizaci\'on de los problemas,
 ya que la mayor\'\i a est\'an dominados por paradigma cliente-servidor que impera
 en el mercado.

En el campo cient\'\i fico es ampliamente utilizado.
 Un ejemplo real es el de estudio oceanogr\'afico realizado por el instituto
 nacional de oceanograf\'\i a espa\~{n}ola\footnote{presentado en un Hispalinux por hacer uso de software libre en su totalidad.}, en este estudio, se utilizaban matrices descomunales para poder controlar
 el movimiento de las part\'\i culas comprendidas en una regi\'on de
 l\'\i quido, que ten\'\i a en cuenta viscosidad, temperatura, presi\'on, viento en la capa
 superficial del liquido, y otras muchas variables. Este ejemplo es el t\'\i pico en el cual PVM y openMosix podr\'\i an ser utilizados conjuntamente.

El ejemplo de c\'odigo paralelizable m\'as t\'\i pico y simple es la multiplicaci\'on de una
 matriz. 
