Interfaz Mpi qué. Intercambio de datos mediante MPI. Trabajar con la biblioteca MPI usando el ejemplo de la biblioteca Intel® MPI. Controladores de errores relacionados con ventanas

El lanzamiento de una aplicación MPI en un clúster informático solo es posible a través del sistema procesamiento por lotes tareas. Para simplificar el lanzamiento y la puesta en cola de un programa paralelo, se proporciona guión especial mpirun. Por ejemplo, se ejecutará mpirun -np 20 ./first.exe programa paralelo first.exe en 20 procesadores, es decir en 5 nodos. (Cada nodo tiene 2 procesadores de doble núcleo). Vale la pena señalar que para lanzar módulo ejecutable ubicado en el directorio actual ($pwd), debe especificar explícitamente la ruta "./" Varias implementaciones de MPI-1 proporcionan un comando de inicio para programas MPI, que tiene la forma mpirun<аргументы mpirun><программа><аргументы программы>

Separar el comando de inicio del programa del programa en sí proporciona flexibilidad, especialmente para implementaciones heterogéneas y en red. Tener un mecanismo de disparo estándar también mejora la portabilidad. Programas IPM un paso más hacia las líneas de comando y los scripts que las controlan. Por ejemplo, un script para un conjunto de programas de validación que ejecuta cientos de programas puede ser un script portátil si se escribe utilizando dicho mecanismo de inicio estándar. Para no confundir el comando "estándar" con el existente en la práctica, que no es estándar ni portátil entre implementaciones, MPI definió mpiexec en lugar de mpirun.

Si bien un mecanismo de lanzamiento estandarizado mejora la usabilidad de MPI, la gama de entornos es muy diversa (por ejemplo, puede que ni siquiera haya una interfaz línea de comando) que el MPI no puede imponer dicho mecanismo. En cambio, MPI define el comando de ejecución mpiexec y lo recomienda, pero no lo exige, como consejo para los desarrolladores. Sin embargo, si una implementación proporciona un comando llamado mpiexec, debe adoptar el formato que se describe a continuación: mpiexec -n <программа>

habrá al menos una forma de correr<программу>con MPI_COMM_WORLD inicial cuyo grupo contiene procesos. Otros argumentos para mpiexec pueden depender de la implementación.

Ejemplo 4.1 Ejecutando 16 instancias de myprog en la máquina actual o predeterminada:

mpiexec -n 16 miprog

3. escribir un programa computación paralela Integral definida de la función 2*(x+2*x*x/1200.0) en el intervalo.

Método del rectángulo izquierdo

doble f(doble x)

(devuelve 2*(x+2*x*x/1200);) // integral iskomyi

int principal(int argc,char **argv)

MPI_Estado de estado;

MPI_Init(&argc,&argv);

MPI_Comm_rank(MPI_COMM_WORLD,&clasificación);

MPI_Comm_size(MPI_COMM_WORLD,&tamaño);

int n=1000,yo,d; // 1000 - uzly

flotar a=0, b=1, h=(b-a)/n,s=0,r=0; //a i b -nachalo i konec otrezka

if (rango!=tamaño-1) // schitaut vse processy, krome poslednego

( para (i=rango*d; i<(rank+1)*d; i++) { s=s+h*f(a+i*h); }

MPI_Send(&s,1,MPI_FLOAT,tamaño-1,1,MPI_COMM_WORLD);)

( para (i=0; yo

( MPI_Recv(&s,1,MPI_FLOAT,i,1,MPI_COMM_WORLD, &status); r+=s; ) )

MPI_Finalizar();)

Surak

1. Arquitecturas de memoria compartida y distribuida.

Memoria compartida distribuida (DSM)

Tradicionalmente, la informática distribuida se basa en un modelo de paso de mensajes, en el que los datos se pasan de un procesador a otro en forma de mensajes. Las llamadas a procedimientos remotos son en realidad el mismo modelo (o muy parecido). DSM es un espacio de direcciones virtuales compartido por todos los nodos (procesadores) de un sistema distribuido. Los programas acceden a los datos en DSM de la misma manera que acceden a los datos en la memoria virtual de las computadoras tradicionales. En sistemas con DSM, los datos se mueven entre las memorias locales de diferentes computadoras de la misma manera que se mueven entre la RAM y la memoria externa de una computadora. La configuración de memoria compartida distribuida es una variante de la memoria distribuida. Aquí, todos los nodos, que constan de uno o más procesadores conectados mediante un esquema SMP, utilizan un espacio de direcciones común. La diferencia entre esta configuración y una máquina con memoria distribuida es que aquí cualquier procesador puede acceder a cualquier parte de la memoria. Sin embargo, el tiempo de acceso a diferentes regiones de memoria varía para cada procesador dependiendo de dónde esté ubicada físicamente la región en el clúster. Por esta razón, estas configuraciones también se denominan máquinas con acceso a memoria no uniforme (NUMA).

Diferencias entre MPI y PVM.

El sistema PVM (Parallel Virtual Machine) se creó para combinar varias estaciones de trabajo en red en una única máquina informática paralela virtual. El sistema es un complemento del sistema operativo UNIX y se utiliza en diversas plataformas de hardware, incluidos sistemas masivamente paralelos. Los sistemas de programación paralela más comunes en la actualidad se basan en MPI (Message Parsing Interface). La idea de MPI es inicialmente simple y obvia. Implica representar un programa paralelo como un conjunto de procesos en ejecución paralelos que interactúan entre sí durante la ejecución de la transferencia de datos mediante procedimientos de comunicación. Constituyen la biblioteca MPI. Sin embargo, la implementación adecuada de MPI para soportar las comunicaciones entre procesadores ha demostrado ser bastante difícil. Esta complejidad está asociada con la necesidad de lograr un alto rendimiento del programa, la necesidad de utilizar numerosos recursos multicomputadores y, como consecuencia, una gran variedad en la implementación de procedimientos de comunicación dependiendo del modo de procesamiento de datos.

  • Tutorial

En esta publicación hablaremos sobre la organización del intercambio de datos utilizando MPI usando el ejemplo de la biblioteca Intel MPI. Creemos que esta información será de interés para cualquiera que quiera familiarizarse con el campo de la computación paralela de alto rendimiento en la práctica.

Proporcionaremos una breve descripción de cómo se organiza el intercambio de datos en aplicaciones paralelas basadas en MPI, así como enlaces a fuentes externas con una descripción más detallada. En la parte práctica, encontrará una descripción de todas las etapas del desarrollo de la aplicación de demostración MPI "Hello World", desde la configuración del entorno necesario hasta el lanzamiento del programa en sí.

MPI (interfaz de paso de mensajes)

MPI es una interfaz de paso de mensajes entre procesos que realizan la misma tarea. Está destinado principalmente a sistemas de memoria distribuida (MPP) a diferencia de, por ejemplo, OpenMP. Un sistema distribuido (clúster), por regla general, es un conjunto de nodos informáticos conectados por canales de comunicación de alto rendimiento (por ejemplo, InfiniBand).

MPI es el estándar de interfaz de datos más común para programación paralela. La estandarización de MPI la lleva a cabo el Foro MPI. Existen implementaciones de MPI para la mayoría de las plataformas, sistemas operativos e idiomas modernos. MPI se utiliza ampliamente para resolver diversos problemas en física computacional, productos farmacéuticos, ciencia de materiales, genética y otros campos del conocimiento.

Desde el punto de vista de MPI, un programa paralelo es un conjunto de procesos que se ejecutan en diferentes nodos informáticos. Cada proceso se genera a partir del mismo código de programa.

La operación principal en MPI es el paso de mensajes. MPI implementa casi todos los patrones de comunicación básicos: punto a punto, colectivo y unilateral.

Trabajando con MPI

Veamos un ejemplo en vivo de cómo se estructura un programa MPI típico. Como aplicación de demostración, tomemos el código fuente de ejemplo que viene con la biblioteca Intel MPI. Antes de ejecutar nuestro primer programa MPI, debemos preparar y configurar un entorno de trabajo para los experimentos.

Configurar un entorno de clúster

Para los experimentos, necesitaremos un par de nodos informáticos (preferiblemente con características similares). Si no tienes dos servidores a mano, siempre puedes utilizar los servicios en la nube.

Para la demostración, elegí el servicio Amazon Elastic Compute Cloud (Amazon EC2). Amazon ofrece a los nuevos usuarios un año de prueba gratuito de servidores de nivel básico.

Trabajar con Amazon EC2 es intuitivo. Si tienes alguna duda, puedes consultar la documentación detallada (en inglés). Si lo desea, puede utilizar cualquier otro servicio similar.

Creamos dos servidores virtuales en funcionamiento. En la consola de administración seleccione Servidores Virtuales EC2 en la Nube, entonces Instancia de lanzamiento("Instancia" significa una instancia de servidor virtual).

El siguiente paso es seleccionar el sistema operativo. La biblioteca Intel MPI es compatible con Linux y Windows. Para conocer por primera vez MPI, elegiremos OS Linux. Elegir Red Hat Enterprise Linux 6.6 de 64 bits o SLES11.3/12.0.
Elegir Tipo de instancia(tipo de servidor). Para experimentos, nos conviene t2.micro (1 vCPU, 2,5 GHz, familia de procesadores Intel Xeon, 1 GiB de RAM). Como usuario registrado recientemente, podría usar este tipo de forma gratuita, marcado como "elegible para el nivel gratuito". Nos fijamos Número de instancias: 2 (número de servidores virtuales).

Después de que el servicio nos solicite ejecutar Instancias de lanzamiento(servidores virtuales configurados), guardamos las claves SSH que serán necesarias para comunicarnos con los servidores virtuales desde el exterior. El estado de los servidores virtuales y las direcciones IP para la comunicación con los servidores informáticos locales se pueden monitorear en la consola de administración.

Punto importante: en la configuración. Red y seguridad / Grupos de seguridad Necesitamos crear una regla que abra puertos para conexiones TCP; esto es necesario para el administrador de procesos MPI. La regla podría verse así:

Tipo: Regla TCP personalizada
Protocolo: TCP
Rango de puertos: 1024-65535
Fuente: 0.0.0.0/0

Por razones de seguridad, puedes establecer una regla más estricta, pero para nuestra demostración esto es suficiente.

Y por último, una breve encuesta sobre posibles temas para futuras publicaciones sobre informática de alto rendimiento.

Sólo los usuarios registrados pueden participar en la encuesta. , Por favor.

Funciones MPI

Tipo derivado, operaciones, tipos de datos

Bsend Buffer_attach Get_count ANY_SOURCE Sendrecv_replace ANY_TAG Sonda

Allgetherv Alltoall Alltoallv Reducir escaneo Rduce_scatter

Un tipo derivado se construye a partir de tipos MPI predefinidos y tipos derivados previamente definidos utilizando funciones de constructor especiales.

MPI_Type_contiguous, MPI_Type_vector, MPI_Type_hvector, MPI_Type_indexed, MPI_Type_hindexed, MPI_Type_struct.

Se registra un nuevo tipo derivado llamando a la función MPI_Type_commit. Sólo después del registro se puede utilizar un nuevo tipo derivado en rutinas de comunicación y en la construcción de otros tipos. Los tipos de MPI predefinidos se consideran registrados.

Cuando un tipo derivado ya no es necesario, se destruye con la función MPI_Type_free.

1) MPI_Init - función de inicialización. Como resultado de ejecutar esta función, se crea un grupo de procesos en el que se ubican todos los procesos de la aplicación y se crea un área de comunicación, descrita por el comunicador predefinido MPI_COMM_WORLD.

MPI_Type_commit - registro de tipo, MPI_Type_free - destrucción de tipo

int MPI_Init(int *argc, char ***argv);

2) MPI_Finalize: función para completar programas MPI. La función cierra todos los procesos MPI y elimina todas las áreas de comunicación.

int MPI_Finalize(nulo);

3) Función para determinar el número de procesos en el área de comunicación. MPI_Comm_size . La función devuelve el número de procesos en el área de comunicación del comunicador.

int MPI_Comm_size(MPI_Comm comm, int *tamaño);

4) Función de detección de número de proceso MPI_Comm_rank . La función devuelve el número del proceso que llamó a esta función. Los números de proceso están en el rango 0..tamaño-1.

int MPI_Comm_rank(comunicación MPI_Comm, int *rango);

5) Función de mensaje MPI_Enviar. La función envía elementos de recuento del tipo de datos del mensaje con etiqueta de identificación para procesar el destino en el área de comunicación del comunicador.

int MPI_Send(void* buf, int count, tipo de datos MPI_Datatype, int dest, int tag, MPI_Comm comm);

6) Función de recepción de mensajes MPI_Recv. La función recibe elementos de recuento del tipo de datos del mensaje con etiqueta de identificación del proceso fuente en el área de comunicación del comunicador.

int MPI_Recv(void* buf, int count, tipo de datos MPI_Datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

7) Función de temporización (temporizador) MPI_Wtime. La función devuelve el tiempo astronómico en segundos, transcurrido desde algún momento del pasado (punto de referencia).

doble MPI_Wtime (nulo)

Las funciones de paso de mensajes entre procesos se dividen en:

Prefijo S (síncrono)

significa modo de transferencia de datos síncrono. La operación de transmisión de datos finaliza sólo cuando finaliza la recepción de datos. La función no es local.

Prefijo B (almacenado en búfer)

significa modo de transferencia de datos almacenados en buffer. En el espacio de direcciones del proceso de envío, utilizando una función especial, se crea un portapapeles, que se utiliza en las operaciones de intercambio. La operación de envío finaliza cuando los datos se colocan en este búfer. La función es de naturaleza local.

Prefijo R (listo)

modo de transmisión de datos acordado o preparado. La operación de transferencia de datos comienza solo cuando el procesador receptor ha establecido la señal de preparación para recibir datos, iniciando la operación de recepción. La función no es local.

Prefijo I (inmediato)

se refiere a operaciones sin bloqueo.

Estructura MPI_Status

Después de leer un mensaje, es posible que se desconozcan algunos parámetros, como la cantidad de elementos leídos, el ID del mensaje y la dirección del remitente. Esta información se puede obtener utilizando el parámetro de estado. Las variables de estado deben declararse explícitamente en el programa MPI. En lenguaje C, el estado es una estructura de tipo MPI_Status con tres campos MPI_SOURCE, MPI_TAG, MPI_ERROR.

8) Para determinar la cantidad de elementos del mensaje realmente recibidos, debe utilizar una función especial MPI_Get_count.

int MPI_Get_count (MPI_Status *estado, tipo de datos MPI_Datatype, int *recuento);

9) Puede determinar los parámetros del mensaje recibido sin leerlo usando la función MPI_Probe. int MPI_Probe (fuente int, etiqueta int, comunicación MPI_Comm, MPI_Status *estado);

10) En situaciones en las que necesita realizar una comunicación mutua entre procesos, es más seguro utilizar una operación combinada. MPI_Sendrecv. En esta operación, los datos enviados desde la matriz buf se reemplazan con los datos recibidos.

int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, MPI_Datatype recvtag, MPI_Comm comm, MPI_Status *status);

11) Función para comprobar la finalización de una operación sin bloqueo MPI_Test.

int MPI_Test(MPI_Request *solicitud, int *bandera, MPI_Status *estado);

Esta es una operación local sin bloqueo. Si la operación asociada con la solicitud se ha completado, se devuelve flag = true y el estado contiene información sobre la operación completada. Si la operación que se está verificando no se ha completado, se devuelve flag = false y el valor de status no está definido en este caso.

12) Función para cancelar una solicitud sin esperar a que se complete una operación sin bloqueo MPI_Request_free.

int MPI_Request_free(MPI_Request *solicitud);

El parámetro de solicitud está establecido en MPI_REQUEST_NULL.

13) Lograr una ejecución eficiente de una operación de transferencia de datos de un proceso a todos los procesos de un programa (difusión de datos) se puede lograr utilizando la función MPI:

int MPI_Bcast(void *buf,int count,MPI_Datatype type,int root,MPI_Comm comm)

La función MPI_Bcast transmite datos desde el búfer buf que contiene elementos de conteo de tipo desde un proceso numerado como raíz a todos los procesos incluidos en el comunicador de comunicaciones.

14) Si necesitas recibir un mensaje de alguien el proceso de envío puede tener el valor MPI_ANY_SOURCE especificado para el parámetro de origen

15) Si es necesario recibir un mensaje con cualquier etiqueta, se puede especificar el valor para el parámetro etiqueta. MPI_ANY_TAG

16) El parámetro de estado le permite definir una serie de características del mensaje recibido:

- status.MPI_SOURCE – rango el proceso de envío del mensaje recibido,

- status.MPI_TAG: etiqueta del mensaje recibido.

17) Función

MPI_Get_count (MPI_Status *estado, MPI_Tipo de tipo de datos, int *recuento)

devuelve en la variable de conteo el número de elementos de tipo tipo en el mensaje recibido.

18) Operaciones que transfieren datos de todos los procesos a un proceso. En esta operación sobre la recogida

los valores realizan uno u otro procesamiento de datos (para enfatizar el último punto, esta operación también se llama operación de reducción de datos)

int MPI_Reduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,int root,MPI_Comm comm)

19) Sincronización de procesos, es decir El logro simultáneo por parte de los procesos de ciertos puntos del proceso de cálculo se garantiza mediante la función MPI: int MPI_Barrier(comunicación MPI_Com); La función MPI_Barrier define una operación colectiva y, por lo tanto, cuando se usa, debe ser llamada por todos los procesos del comunicador usado. Al llamar a la función MPI_Barrier

la ejecución del proceso está bloqueada; los cálculos del proceso continuarán solo después de que todos los procesos del comunicador hayan llamado a la función MPI_Barrier.

20) Para utilizar el modo de transferencia almacenada en búfer, se debe crear y transferir un búfer de memoria MPI

para almacenar mensajes en búfer: la función utilizada para esto se ve así: int MPI_Buffer_attach (void *buf, int size),

- búfer de memoria buf para almacenar mensajes en búfer,

- tamaño: tamaño del búfer.

21) Después de terminar de trabajar con el buffer, se debe desconectar de MPI usando la función:

int MPI_Buffer_detach (void *buf, int *tamaño).

22) Lograr una ejecución simultánea eficiente y garantizada de las operaciones de transmisión y recepción de datos se puede lograr utilizando la función MPI:

int MPI_Sendrecv (void *sbuf,int scount,MPI_Datatype stype,int dest, int stag, void *rbuf,int rcount,MPI_Datatype

rtype, int fuente, int rtag, MPI_Comm comm, MPI_Status *estado)

23) Cuando los mensajes son del mismo tipo, MPI tiene la capacidad de utilizar un único buffer: intMPI_Sendrecv_replace (void *buf, int count, MPI_Datatype type, int dest,

int stag, int source, int rtag, MPI_Comm comm, MPI_Status* estado)

24) La operación generalizada de transmitir datos de un proceso a todos los procesos (distribución de datos) se diferencia de la transmisión en que el proceso transmite diferentes datos a los procesos (ver Fig. 4.4). Esta operación se puede realizar usando la función:

int MPI_Scatter (void *sbuf,int scount,tipo MPI_Datatype,

25) La operación de transferencia de datos generalizada desde todos los procesadores a un proceso (recopilación de datos) es la inversa del procedimiento de distribución de datos (ver Fig. 4.5). Para realizar esta operación en MPI existe una función:

int MPI_Gather (void *sbuf,int scount,tipo MPI_Datatype,

void *rbuf,int rcount,MPI_Datatype rtype, int root, MPI_Comm comm)

26) Cabe señalar que cuando se utiliza la función MPI_Gather, la recopilación de datos se lleva a cabo únicamente

en un proceso. Obtener todos los datos recopilados sobre cada uno de los procesos del comunicador.

necesitas usar la función de recolección y distribución:

int MPI_Allgather (void *sbuf, int scount, tipo MPI_Datatype, void *rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm)

27) Transferir datos de todos los procesos a todos los procesos es la operación de transferencia de datos más común (ver Figura 4.6). Esta operación se puede realizar usando la función:

int MPI_Alltoall (void *sbuf,int scount,MPI_Datatype stype, void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm)

28) La función MPI_Reduce proporciona resultados de reducción de datos

sólo en un proceso. Para obtener los resultados de la reducción de datos de cada uno de los procesos del comunicador, se debe utilizar la función de reducción y distribución:

int MPI_Allreduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

29) Y se puede obtener otra versión de la operación de recopilación y procesamiento de datos, que garantiza que se obtengan todos los resultados de reducción parcial, utilizando la función:

int MPI_Scan (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

El diagrama general de ejecución de la función MPI_Scan se muestra en la Fig. 4.7. Los elementos de los mensajes recibidos representan los resultados del procesamiento de los elementos correspondientes de los mensajes transmitidos por los procesos y para obtener resultados en un proceso con rango i, 0≤i

30) El valor inicial de la variable bufpos debe formarse antes de que comience el empaquetado y luego lo establece la función Paquete_MPI. La función MPI_Pack se llama secuencialmente para empaquetar todos los datos necesarios.

int MPI_Pack_size (recuento int, tipo MPI_Datatype, comunicación MPI_Comm, int *tamaño)

31) Después de empaquetar todos los datos necesarios, el búfer preparado se puede utilizar en funciones de transferencia de datos con el tipo MPI_PACKED especificado.

Después de recibir un mensaje con tipo MPI_PACKED, los datos se pueden descomprimir usando la función:

int MPI_Unpack (void *buf, int bufsize, int *bufpos, void *data, int count, MPI_Datatype type, MPI_Comm comm)

Computadora con conjunto de instrucciones complejas

CISC (computación con conjunto de instrucciones complejas en inglés, o computadora con conjunto de instrucciones complejas en inglés)

computadora con un conjunto completo de instrucciones) es un concepto de diseño de procesador que se caracteriza por el siguiente conjunto de propiedades:

un número relativamente pequeño de registros de propósito general;

· una gran cantidad de instrucciones de máquina, algunas de las cuales se cargan semánticamente de manera similar a los operadores de lenguajes de programación de alto nivel y se ejecutan en muchos ciclos de reloj;

· una gran cantidad de métodos de direccionamiento;

· una gran cantidad de formatos de comandos de varios tamaños de bits;

· el predominio del formato de comando de dos direcciones;

· presencia de comandos de procesamiento de tipos memoria-registro.

Defectos :

alto costo de hardware; dificultades con la paralelización de cálculos.

La técnica de construcción del sistema de instrucción CISC es lo opuesto a otra técnica: RISC. La diferencia entre estos conceptos radica en los métodos de programación, no en la arquitectura real del procesador. Casi todos los procesadores modernos emulan conjuntos de instrucciones de tipo RISC y CISC.

Conjunto de instrucciones reducido Computadora

Se basa en los principios de la arquitectura RISC: formato de instrucción fijo, operaciones de registro, ejecución de instrucciones de un solo ciclo, métodos de direccionamiento simples y un archivo de registro de gran tamaño. Al mismo tiempo, existen varias características importantes que distinguen esta arquitectura de las arquitecturas de otros procesadores RISC. Estos incluyen: un conjunto independiente de registros para cada uno de los actuadores; inclusión de instrucciones individuales tipo CISC en el sistema; falta de un mecanismo de “transición retrasada”; una forma original de implementar saltos condicionales. Las principales aplicaciones de las arquitecturas de microprocesadores son los servidores y supercomputadoras de alto rendimiento.

Estas computadoras se basaban en una arquitectura que separaba las instrucciones de procesamiento de las instrucciones de memoria y enfatizaba la canalización eficiente. El sistema de instrucción fue diseñado de tal manera que la ejecución de cualquier instrucción requiriera una pequeña cantidad de ciclos de máquina (preferiblemente un ciclo de máquina). La lógica misma para ejecutar comandos con el fin de aumentar el rendimiento se centró en la implementación del hardware más que del firmware. Para simplificar la lógica de decodificación de comandos, se utilizaron comandos de longitud fija.

Y formato fijo.

EN ¿Cuál es el objetivo de la tecnología de búfer de dirección de destino de transición?

EN El procesador tiene un mecanismo para predecir dinámicamente la dirección de las transiciones. con esto

El objetivo en el chip es una pequeña memoria caché llamada búfer de destino de rama (BTB) y dos pares independientes de búferes de captación previa de instrucciones (dos búferes de 32 bits por canalización). El búfer de dirección de destino de rama almacena las direcciones de las instrucciones que se encuentran en los búferes de captación previa. El funcionamiento de los buffers de captación previa está organizado de tal manera que en un momento dado, las instrucciones se recuperan sólo en uno de los buffers del par correspondiente. Cuando se detecta una operación de bifurcación en el flujo de instrucciones, la dirección de bifurcación calculada se compara con las direcciones almacenadas en el búfer BTB. Si hay una coincidencia, se predice que se realizará la bifurcación y se habilita otro búfer de captación previa y comienza a emitir comandos a la canalización correspondiente para su ejecución. Si hay una discrepancia, se supone que la rama no se ejecutará y el búfer de captación previa no se cambiará, continuando con el orden normal de emisión de comandos. Esto evita el tiempo de inactividad del transportador.

Conflictos estructurales y formas de minimizarlos

El modo combinado de ejecución de comandos generalmente requiere la canalización de unidades funcionales y la duplicación de recursos para resolver todas las combinaciones posibles de comandos en la canalización. Si alguna combinación de comandos falla

ser aceptado debido a un conflicto de recursos, entonces se dice que la máquina tiene un conflicto estructural. El ejemplo más típico de máquinas en las que pueden surgir conflictos estructurales son las máquinas con dispositivos funcionales que no están completamente transportadores.

Minimización: la canalización detiene la ejecución de uno de los comandos hasta que el dispositivo requerido esté disponible.

Conflictos de datos, paradas de tuberías e implementación del mecanismo de derivación

Uno de los factores que tiene un impacto significativo en el rendimiento de los sistemas transportadores son las dependencias lógicas entre instrucciones. Los conflictos de datos surgen cuando el uso de canalización puede cambiar el orden de acceso a los operandos de modo que este orden sea diferente del orden que se observa cuando las instrucciones se ejecutan secuencialmente en una máquina no canalizada. El problema planteado en este ejemplo se puede resolver utilizando una técnica de hardware bastante simple llamada reenvío de datos, omisión de datos o, a veces, cortocircuito.

Conflictos de datos que hacen que la canalización se detenga

En su lugar, necesitamos hardware adicional, llamado hardware de interlook de canalización, para garantizar que el ejemplo se ejecute correctamente. En general, este tipo de equipos detecta conflictos y detiene el proceso mientras exista un conflicto. En este caso, este hardware pausa el pipeline comenzando con la instrucción que quiere utilizar los datos mientras la instrucción anterior, cuyo resultado es un operando nuestro, produce ese resultado. Este equipamiento provoca el estancamiento de una línea de producción o la aparición de una "burbuja" del mismo modo que en el caso de los conflictos estructurales.

Búfers de predicción de rama condicional

El buffer de predicción de bifurcación condicional es una pequeña memoria direccionable por los bits menos significativos de la dirección de la instrucción de bifurcación. Cada celda de esta memoria contiene un bit, que indica si la rama anterior se ejecutó o no. Este es el tipo de buffer más simple de este tipo. No tiene etiquetas y solo es útil para reducir la latencia de la sucursal si la latencia es mayor que el tiempo requerido para calcular el valor de la dirección de destino de la sucursal. El búfer de predicción de rama se puede implementar como un pequeño caché dedicado al que accede la dirección de instrucción durante la etapa de búsqueda de instrucciones de la canalización (IF), o como un par de bits asociados con cada bloque de caché de instrucciones y recuperados con cada instrucción.

Paralelización en lenguaje C
Ejemplo 3b. Paralelización en Fortran
Ejemplo 4a. Determinación de las características del temporizador del sistema en lenguaje C.
Ejemplo 4b. Definición de las características del temporizador del sistema en Fortran

1.4. Enviar y recibir mensajes entre procesos separados

1.4.1. Operaciones punto a punto

1.4.2. Enviar y recibir mensajes con bloqueo

Ejemplo 5a. Intercambio de mensajes entre dos procesos en lenguaje C
Ejemplo 5b. Intercambio de mensajes entre dos procesos en Fortran
Ejemplo 6a. Intercambio de mensajes entre procesos pares e impares en C
Ejemplo 6b. Intercambio de mensajes entre procesos pares e impares en Fortran
Ejemplo 7a. Reenvío a un proceso inexistente en C
Ejemplo 7b. Reenvío a un proceso inexistente en Fortran
Ejemplo 8a. Envío de datos buffer en lenguaje C
Ejemplo 8b. Envío de datos buffer en lenguaje Fortran
Ejemplo 9a. Obtención de información sobre atributos de mensajes en lenguaje C
Ejemplo 9b. Obtención de información sobre atributos de mensajes en Fortran
Ejemplo 10a. Definición de latencia y rendimiento en lenguaje C.
Ejemplo 10b. Definición de latencia y rendimiento en Fortran

1.4.3. Enviar y recibir mensajes sin bloquear

Ejemplo 11a. Intercambio sobre una topología en anillo utilizando operaciones sin bloqueo en C
Ejemplo 11b. Intercambio sobre una topología en anillo utilizando operaciones sin bloqueo en Fortran
Ejemplo 12a. Esquema de comunicación "maestro - trabajadores" en lenguaje C
Ejemplo 12b. Diagrama de comunicación "maestro - trabajadores" en lenguaje Fortran
Ejemplo 13a. Transposición de matrices en lenguaje C.
Ejemplo 13b. Transponer una matriz en Fortran

1.4.4. Solicitudes de interacción pendientes

Ejemplo 14a. Esquema de un método iterativo con intercambio a lo largo de una topología en anillo utilizando consultas diferidas en lenguaje C
Ejemplo 14b. Esquema de un método iterativo con intercambio sobre una topología en anillo utilizando consultas diferidas en Fortran

1.4.5. Situaciones de punto muerto

Ejemplo 15a. Intercambio sobre una topología en anillo utilizando el procedimiento MPI_Sendrecv en lenguaje C
Ejemplo 15b. Intercambio sobre una topología en anillo utilizando el procedimiento MPI_SENDRECV en Fortran

1.5. Interacciones de procesos colectivos

1.5.1. Disposiciones generales

1.5.2. Barrera

Ejemplo 16a. Modelado de sincronización de barreras en lenguaje C.
Ejemplo 16b. Modelado de sincronización de barreras en Fortran

1.5.3. Operaciones de transferencia colectiva de datos

1.5.4. Operaciones globales

Ejemplo 17a. Modelado de suma global usando un esquema de duplicación y la operación colectiva MPI_Reduce en lenguaje C
Ejemplo 17b. Modelado de suma global usando un esquema de duplicación y la operación colectiva MPI_Reduce en Fortran

1.5.5. Operaciones globales personalizadas

Ejemplo 18a. Función global personalizada en lenguaje C
Ejemplo 18b. Función global personalizada en Fortran

1.6. Grupos y comunicadores

1.6.1. Disposiciones generales

1.6.2. Operaciones con grupos de procesos.

Ejemplo 19a. Trabajar con grupos en lenguaje C.
Ejemplo 19b. Trabajar con grupos en Fortran

1.6.3. Operaciones con comunicadores.

Ejemplo 20a. Rompiendo un comunicador en C
Ejemplo 20b. Particionar un comunicador en Fortran
Ejemplo 21a. Renumeración de procesos en lenguaje C.
Ejemplo 21b. Renumerar procesos en Fortran

1.6.4. Intercomunicadores

Ejemplo 22a. Esquema maestro-trabajador utilizando un intercomunicador en lenguaje C.
Ejemplo 22b. Circuito maestro-trabajador mediante intercomunicador en Fortran

1.6.5. Atributos

1.7. Topologías virtuales

1.7.1. Disposiciones generales

1.7.2. topología cartesiana

1.7.3. Topología gráfica

Ejemplo 23a. Diagrama maestro-trabajador usando topología de gráficos en lenguaje C
Ejemplo 23b. Esquema maestro-trabajador usando topología gráfica en Fortran

1.8. Envío de diferentes tipos de datos

1.8.1. Disposiciones generales

1.8.2. Tipos de datos derivados

Ejemplo 24a. Reorganizar las columnas de la matriz en orden inverso en lenguaje C
Ejemplo 24b. Reorganizar las columnas de la matriz en orden inverso en Fortran

1.8.3. Empaquetado de datos

Ejemplo 25a. Envío de datos empaquetados en lenguaje C
Ejemplo 25b. Envío de datos empaquetados en Fortran

1.9. objeto de información

1.9.1. Disposiciones generales

1.9.2. Trabajar con el objeto de información

1.10. Control dinámico de procesos

1.10.1. Disposiciones generales

1.10.2.Creación de procesos

maestro.c
esclavo.c
Ejemplo 26a. Esquema maestro-trabajador que utiliza el proceso de generación en lenguaje C
maestro.f
esclavo.f
Ejemplo 26b. Esquema maestro-trabajador que utiliza el proceso de generación en Fortran

1.10.3. Comunicación cliente-servidor

servidor.c
cliente.c
Ejemplo 27a. Intercambio de datos entre servidor y cliente utilizando un nombre público en lenguaje C
servidor.f
cliente.f
Ejemplo 27b. Intercambio de datos entre servidor y cliente utilizando nombre público en lenguaje Fortran

1.10.4. Eliminar una asociación de proceso

1.10.5. Comunicación de enchufe

1.11. Comunicaciones unidireccionales

1.11.1. Disposiciones generales

1.11.2. Trabajando con una ventana

1.11.3. Transferencia de datos

1.11.4. Sincronización

Ejemplo 28a
Ejemplo 28b
Ejemplo 29a. Intercambio a través de una topología en anillo utilizando comunicaciones unidireccionales en C
Ejemplo 29b. Intercambio sobre una topología en anillo utilizando comunicaciones unidireccionales en Fortran
Ejemplo 30a. Intercambio a través de una topología en anillo utilizando comunicaciones unidireccionales en C
Ejemplo 30b. Intercambio sobre una topología en anillo utilizando comunicaciones unidireccionales en Fortran

1.12. Interfaces externas

1.12.1. Consultas generales

1.12.2. Información del estado

1.12.3. Trapos

1.13. E/S paralelas

1.13.1. Definiciones

1.13.2. Trabajar con archivos

1.13.3. Acceso a datos

Ejemplo 31a. Lectura en buffer de un archivo en lenguaje C
Ejemplo 31b. Lectura almacenada en búfer de un archivo en Fortran
Ejemplo 32a. Lectura colectiva de un archivo en lenguaje C.
Ejemplo 32b. Lectura colectiva de un archivo en Fortran

1.14. Manejo de errores

1.14.1. Disposiciones generales

1.14.2. Manejadores de errores asociados con comunicadores

1.14.3. Controladores de errores relacionados con ventanas

1.14.4. Manejadores de errores relacionados con archivos

1.14.5. Trámites adicionales

1.14.6. Códigos y clases de error

1.14.7. Llamar a controladores de errores

Ejemplo 33a. Manejo de errores en lenguaje C.
Ejemplo 33b. Manejo de errores en Fortran

Capítulo 2 Tecnología de programación paralela OpenMP

2.1. Introducción

2.2. Conceptos básicos

2.2.1. Compilando un programa

Ejemplo 34a. Compilación condicional en C
Ejemplo 34b
Ejemplo 34c. Compilación condicional en Fortran

2.2.2. Modelo de programa paralelo

2.2.3. Directivas y procedimientos

2.2.4. Ejecución del programa

2.2.5. Momento

Ejemplo 35a. Trabajar con temporizadores del sistema en C
Ejemplo 35b. Trabajar con temporizadores del sistema en Fortran

2.3. Áreas paralelas y seriales

2.3.1. directiva paralela

Ejemplo 36a. Región paralela en lenguaje C
Ejemplo 36b. Región paralela en Fortran
Ejemplo 37a. La opción de reducción en lenguaje C.
Ejemplo 37b. La opción de reducción en Fortran

2.3.2. Notación taquigráfica

2.3.3. Variables de entorno y procedimientos auxiliares

Ejemplo 38a. Procedimiento omp_set_num_threads y opción num_threads en lenguaje C
Ejemplo 38b. Procedimiento omp_set_num_threads y opción num_threads en lenguaje Fortran
Ejemplo 39a. Procedimientos omp_set_dynamic y omp_get_dynamic en lenguaje C
Ejemplo 39b. Procedimientos omp_set_dynamic y omp_get_dynamic en Fortran
Ejemplo 40a. Regiones paralelas anidadas en C
Ejemplo 40b. Regiones paralelas anidadas en Fortran
Ejemplo 41a. Función omp_in_parallel en lenguaje C
Ejemplo 41b. Función omp_in_parallel en lenguaje Fortran

2.3.4. directiva única

Ejemplo 42a. Directiva única y opción nowait en lenguaje C
Ejemplo 42b. Directiva única y opción no esperar en Fortran
Ejemplo 43a. Opción copyprivate en lenguaje C
Ejemplo 43b. opción copyprivate en Fortran

2.3.5. directiva maestra

Ejemplo 44a. Directiva maestra en lenguaje C.
Ejemplo 44b. directiva maestra en Fortran

2.4. modelo de datos

Ejemplo 45a. Opción privada en lenguaje C.
Ejemplo 45b. La opción privada en Fortran
Ejemplo 46a. Opción compartida en lenguaje C.
Ejemplo 46b. La opción compartida en Fortran
Ejemplo 47a. primera opción privada en lenguaje C
Ejemplo 47b. primera opción privada en Fortran
Ejemplo 48a. directiva threadprivate en lenguaje C
Ejemplo 48b. directiva threadprivate en Fortran
Ejemplo 49a. Opción de copiar en lenguaje C.
Ejemplo 49b. opción de copia en Fortran

2.5. Distribución del trabajo

2.5.1. Paralelización de bajo nivel

Ejemplo 50a. Procedimientos omp_get_num_threads y omp_get_thread_num en lenguaje C
Ejemplo 50b. Procedimientos omp_get_num_threads y omp_get_thread_num en Fortran

2.5.2. Bucles paralelos

Ejemplo 51a. para directiva en lenguaje C
Ejemplo 51b. La directiva do en Fortran
Ejemplo 52a. Opción de programación en lenguaje C
Ejemplo 52b. opción de horario en Fortran
Ejemplo 53a. Opción de programación en lenguaje C

Anotación: La conferencia está dedicada a la consideración de la tecnología MPI como estándar de programación paralela para sistemas de memoria distribuida. Se consideran los principales modos de transmisión de datos. Se introducen conceptos como grupos de procesos y comunicadores. Cubre tipos de datos básicos, operaciones punto a punto, operaciones colectivas, operaciones de sincronización y mediciones de tiempo.

Objetivo de la conferencia: La conferencia tiene como objetivo estudiar la metodología general para el desarrollo de algoritmos paralelos.

Grabación de vídeo de la conferencia - (volumen - 134 MB).

5.1. MPI: conceptos básicos y definiciones

Consideremos una serie de conceptos y definiciones que son fundamentales para el estándar MPI.

5.1.1. El concepto de programa paralelo.

Bajo programa paralelo En el marco de MPI, entendemos un conjunto de acciones ejecutadas simultáneamente. procesos. Los procesos se pueden ejecutar en diferentes procesadores, pero también se pueden ubicar varios procesos en el mismo procesador (en este caso, se ejecutan en modo de tiempo compartido). En el caso extremo, se puede utilizar un solo procesador para ejecutar un programa paralelo; por regla general, este método se utiliza para comprobar inicialmente la corrección del programa paralelo.

Cada proceso de un programa paralelo se genera a partir de una copia del mismo código de programa ( modelo SPMP). Este código de programa, presentado en forma de programa ejecutable, debe estar disponible en el momento en que se inicia el programa paralelo en todos los procesadores utilizados. El código fuente del programa ejecutable se desarrolla en los lenguajes algorítmicos C o Fortran utilizando una u otra implementación de la biblioteca MPI.

La cantidad de procesos y la cantidad de procesadores utilizados se determinan en el momento en que se inicia el programa paralelo utilizando el entorno de ejecución del programa MPI y no pueden cambiar durante los cálculos (el estándar MPI-2 brinda la posibilidad de cambiar dinámicamente la cantidad de procesos). Todos los procesos del programa están numerados secuencialmente del 0 al p-1, Dónde pag es el número total de procesos. El número de proceso se llama rango proceso.

5.1.2. Operaciones de transferencia de datos

MPI se basa en operaciones de paso de mensajes. Entre las funciones proporcionadas como parte de MPI, existen diferentes dobles (punto a punto) operaciones entre dos procesos y colectivo (colectivo) acciones de comunicación para la interacción simultánea de varios procesos.

Para realizar operaciones emparejadas, se pueden utilizar diferentes modos de transmisión, incluido el síncrono, el bloqueo, etc.; una consideración completa de las posibles modos de transmisión se realizará en la subsección 5.3.

Como se señaló anteriormente, el estándar MPI prevé la necesidad de implementar la mayoría de las operaciones básicas de transferencia de datos colectivos; consulte las subsecciones 5.2 y 5.4.

5.1.3. Concepto de comunicadores

Los procesos de un programa paralelo se combinan en grupos. Bajo comunicador MPI se refiere a un objeto de servicio especialmente creado que combina un grupo de procesos y una serie de parámetros adicionales ( contexto) utilizado al realizar operaciones de transferencia de datos.

Normalmente, las operaciones de transferencia de datos emparejadas se realizan para procesos que pertenecen al mismo comunicador. Las operaciones colectivas se aplican simultáneamente a todos los procesos del comunicador. Como resultado, especificar el comunicador a utilizar es obligatorio para las operaciones de transferencia de datos en MPI.

Durante los cálculos, se pueden crear nuevos grupos de procesos y comunicadores y eliminar los existentes. Un mismo proceso puede pertenecer a diferentes grupos y comunicadores. Todos los procesos presentes en el programa paralelo están incluidos en el comunicador creado por defecto con el identificador MPI_COMM_WORLD.

Si es necesario transferir datos entre procesos de diferentes grupos, es necesario crear un comunicador global ( intercomunicador).

En la subsección 5.6 se realizará una discusión detallada de las capacidades de MPI para trabajar con grupos y comunicadores.

5.1.4. Tipos de datos

Al realizar operaciones de paso de mensajes, debe especificar los datos que se enviarán o recibirán en las funciones MPI. tipo datos enviados. MPI contiene un gran conjunto tipos básicos datos que coinciden en gran medida con los tipos de datos en los lenguajes algorítmicos C y Fortran. Además, MPI tiene la capacidad de crear nuevos tipos derivados datos para una descripción más precisa y concisa del contenido de los mensajes reenviados.

En la subsección 5.5 se realizará una discusión detallada de las capacidades de MPI para trabajar con tipos de datos derivados.

5.1.5. Topologías virtuales

Como se señaló anteriormente, las operaciones de transferencia de datos emparejadas se pueden realizar entre cualquier proceso del mismo comunicador, y todos los procesos del comunicador participan en una operación colectiva. En este sentido, la topología lógica de las líneas de comunicación entre procesos tiene la estructura de un gráfico completo (independientemente de la presencia de canales de comunicación físicos reales entre procesadores).

Al mismo tiempo (y esto ya se señaló en la Sección 3), para la presentación y posterior análisis de una serie de algoritmos paralelos, es aconsejable tener una representación lógica de la red de comunicación existente en forma de determinadas topologías.

MPI tiene la capacidad de representar múltiples procesos en la forma rejas dimensión arbitraria (ver subsección 5.7). En este caso, los procesos límite de las celosías se pueden declarar vecinos y, por lo tanto, en función de las celosías, estructuras del tipo toro.

Además, MPI cuenta con herramientas para generar topologías lógicas (virtuales) de cualquier tipo requerido. En la subsección 5.7 se realizará una discusión detallada de las capacidades de MPI para trabajar con topologías.

Y finalmente, una última serie de notas antes de empezar a analizar MPI:

  • Las descripciones de funciones y todos los ejemplos de programas proporcionados se presentarán en el lenguaje algorítmico C; Las características del uso de MPI para el lenguaje algorítmico Fortran se darán en la sección 5.8.1.
  • En la sección 5.8.2 se analizará una breve descripción de las implementaciones disponibles de las bibliotecas MPI y una descripción general del entorno de ejecución de los programas MPI.
  • La presentación principal de las capacidades de MPI se centrará en la versión 1.2 del estándar ( MPI-1); Las propiedades adicionales del estándar versión 2.0 se presentarán en la cláusula 5.8.3.

Al comenzar a estudiar MPI, se puede observar que, por un lado, MPI es bastante complejo: el estándar MPI prevé la presencia de más de 125 funciones. Por otro lado, la estructura de MPI está cuidadosamente pensada: el desarrollo de programas paralelos puede comenzar después de considerar sólo 6 funciones de MPI. Todas las funciones adicionales de MPI se pueden dominar a medida que aumenta la complejidad de los algoritmos y programas desarrollados. Es en este estilo, desde lo simple hasta lo complejo, que se presentará todo el material educativo sobre MPI.

5.2. Introducción al desarrollo de programas paralelos utilizando MPI

5.2.1. Conceptos básicos del IPM

Presentemos el conjunto mínimo requerido de funciones MPI, suficiente para el desarrollo de programas paralelos bastante simples.

5.2.1.1 Inicialización y terminación de programas MPI

Primera función llamada MPI debería ser una función:

int MPI_Init (int *agrc, char ***argv);

para inicializar el entorno de ejecución del programa MPI. Los parámetros de la función son el número de argumentos en la línea de comando y el texto de la línea de comando misma.

Última función llamada MPI debe ser una función:

int MPI_Finalize(nulo);

Como resultado, se puede observar que la estructura de un programa paralelo desarrollado utilizando MPI debe tener la siguiente forma:

#incluye "mpi.h" int main (int argc, char *argv) (<программный код без использования MPI функций>MPI_Init(&agrc, &argv);<программный код с использованием MPI функций>MPI_Finalize();<программный код без использования MPI функций>devolver 0; )

Cabe señalar:

  1. Archivo mpi.h contiene definiciones de constantes con nombre, prototipos de funciones y tipos de datos de la biblioteca MPI,
  2. Funciones MPI_Inicio Y MPI_Finalizar son obligatorios y deben ser ejecutados (y sólo una vez) por cada proceso del programa paralelo,
  3. antes de la llamada MPI_Inicio La función se puede utilizar. MPI_Inicializado para determinar si se ha realizado una llamada previamente MPI_Inicio.

Los ejemplos de funciones discutidos anteriormente dan una idea de la sintaxis para nombrar funciones en MPI. El nombre de la función está precedido por el prefijo MPI, seguido de una o más palabras del nombre, la primera palabra del nombre de la función comienza con un carácter mayúscula y las palabras están separadas por un guión bajo. Los nombres de las funciones MPI, por regla general, explican el propósito de las acciones realizadas por la función.

Cabe señalar:

  • Comunicador MPI_COMM_WORLD, como se señaló anteriormente, se crea de forma predeterminada y representa todos los procesos del programa paralelo que se está ejecutando,
  • Rango obtenido usando la función MPI_Comm_rank, es el rango del proceso que realizó la llamada a esta función, es decir variable ProcRank Tomará diferentes valores en diferentes procesos.



Arriba