Computação Paralela - Atividade 05

Esta atividade deve ser colocada no repositório, nas pasta atividades/atividade05 sob os nomes de arquivos indicados na questão.

Valor: 2,5 Pontos.

Data de Entrega: 07/09/2020.

Questão 01 - 0,5 Ponto.

Considere a seguinte aplicação MPI. Nela, o processo 0 define uma quantidade aleatória de números (entre 0 e MAX-1) e os envia para o processo 1.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <mpi.h>
                        
#define MAX 100
                        
int main(int argc, char *argv[]) {
    int rank, total_num, tag = 0;
    int source = 0, destination = 1, numbers[MAX];
                        
    MPI_Status status;
                        
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
                        
    if (rank == source) {
        srand(time(0));
        total_num = (rand() / (float) RAND_MAX) * MAX;
                        
        MPI_Send(numbers, total_num, MPI_INT, destination, tag, MPI_COMM_WORLD);
                        
        printf("Process %d sent %d numberts to 1\n", source, total_num);
    } else if (rank == destination) {
        MPI_Recv(numbers, MAX, MPI_INT, source, tag, MPI_COMM_WORLD, &status);
        MPI_Get_count(&status, MPI_INT, &total_num);
                        
        printf("Process %d received %d numbers. Source = %d, tag = %d\n", destination, total_num, status.MPI_SOURCE, status.MPI_TAG);
    }
                        
    MPI_Finalize();
                   
    return 0;
}
        

Este código utiliza a função MPI_Get_count, que recebe uma variável do tipo MPI_Status preenchida por uma chamada MPI_Recv anterior e retorna a quantidade de valores recebidos. Os valores dos números não importam, pois queremos apenas exercitar as funções do MPI.

Você deve modificar o código para que seja executado com um número qualquer de processos, e que todos os processos com rank diferente de 0 enviem uma mensagem com tamanho aleatório para o processo 0. Cada processo deve informar na tela quantos números está enviando ao 0. Quando terminar de receber todas as mensagens, o processo 0 deve também informar quantos números recebeu de cada outro processo. Salve o arquivo no repositório sob o nome questao01.c.

Dica: você pode usar MPI_ANY_SOURCE e MPI_ANY_TAG para facilitar a troca de mensagens.

Questão 02 - 0,5 Ponto.

O programa a seguir implementa Método do Trapézio para fazer o cálculo da integral de uma função (exp) em um intervalo (limitado por a e b).

#include <stdio.h>
#include <math.h>
#include <mpi.h>

double f(double x) {
    double return_val;
    return_val = exp(x);
    return return_val;
}

int main(int argc, char *argv[]) {
    int rank, size;
    double a = 0.0, b = 1.0;
    long int n = 100000000;
    double x,h;
    double integral = 0.0, total;
    int source, destination = 0;
    int tag = 3;

    MPI_Status status;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    h = (b - a)/n;

    if (rank == 0)
        integral = (f(a) + f(b)) / 2.0;

    for (x = a + h * (rank + 1); x < b; x += size * h) {
        integral += f(x);
    }

    integral = integral * h;

    if (rank == 0) {
        total = integral;
        for (source = 1; source < size; source++) {
            MPI_Recv(&integral, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &status);
            total += integral;
        }
    } else {
        MPI_Send(&integral, 1, MPI_DOUBLE, destination, tag, MPI_COMM_WORLD);
    }

    if (rank == 0) {
        printf("With n = %ld.\n", n);
        printf("Integral from %lf to %lf = %lf \n", a, b, total);
    }

    MPI_Finalize();

    return 0;
}
         

Antes de continuar, salve o código no arquivo questao02.c e tenha certeza que você entende como as iterações do laço estão sendo distribuídas entre os processos. O segredo para essa compreensão é entender o primeiro laço for.

Após compreender a distribuição dos processos, execute o programa com 1, 2, 4 e 8 processos. Use sua própria máquina, independente de quantos núcleos ela tem.

Se as execuções ocorrerem com sucesso, instrumente o código com o uso da rotina MPI_Wtime e anote o tempo de execução para cada número de processos. Não se preocupe se os valores estiverem muito próximos ou semelhantes.

Armazene o tempo de execução no arquivo questao02.txt com o formato abaixo:

# Core i5 4440
# Processos    Tempo
1               ...
2               ...
4               ...
6               ...
8               ...
        

Questão 03 - 0,5 Ponto.

Escreva um programa para calcular o produto escalar de dois vetores. Utilize rotinas MPI_Send e MPI_Recv para comunicação entre os processos. Considere cada vetor com N posições, informado como argumento na linha de comando, e divida a operação entre P processos distintos. Considere que a dividação de N por P não tem resto. Atribua os valores iniciais aos vetores localmente e assuma que todos os processos conhecem o tamanho N total do vetor. Salve o resultado no arquivo questao03.c.

Questão 04 - 0,5 Ponto.

Execute o programa do exercício anterior com N = 1.000.000 e com 1, 2, 4 e 8 processos, na sua própria máquina. Instrumente o código com uso da rotina MPI_Wtime(). Faça um novo arquivo questao04.txt no mesmo formato da Questão 02.

Questão 05 - 0,5 Ponto.

Considere uma topologia em anel para N processos. Ou seja processo 0 está "conectado" ao processo 1, processo 1 ao processo 2, assim sucessivamente, até o processo N - 1 se conectar ao processo 0. Esta "ligação" não é fixa, trata-se do fluxo das mensagens.

Escreva um programa chamado questao05.c utilizando as rotinas MPI_Send e MPI_Recv para comunicação entre os processos que faça circular uma mensagem contendo um inteiro positivo ao longo desse canal. O processo com rank 0 é quem inicia a transmissão e cada vez que a mensagem passa por ele novamente o valor contido na mensagem deve ser decrementando até chegar ao valor 0. Quando um processo receber a mensagem com valor 0 ele deverá passá-la adiante e então terminar sua execução. O valor inicial deve ser informado como argumento na linha de comando.

Dica: para identificar os nós vizinhos no anel faça:

destination = (rank + 1) % size;
source = (rank + size - 1) % size;