Aula 01: A importância da linguagem¶
Nesta atividade, veremos como a escolha da linguagem de programação pode melhorar significativamente o desempenho de operações computacionalmente intensivas. Utilizaremos Python e C++ para implementar e comparar o desempenho na multiplicação de matrizes, uma operação comum em muitas aplicações de IA e visão computacional.
Parte 1: Implementação Básica em Python¶
Execute o código abaixo e observe o tempo de execução. Essa é uma implementação sequencial que processa a multiplicação de matrizes usando uma única thread em python.
import time # Importa o módulo time, que fornece funções para medir o tempo de execução do código.
def multiply_matrices(A, B):
# Define a função multiply_matrices, que realiza a multiplicação de duas matrizes A e B.
# O resultado é armazenado na matriz result.
result = [[0 for _ in range(len(B[0]))] for _ in range(len(A))]
# Cria uma matriz de zeros com o mesmo número de linhas que A e o mesmo número de colunas que B.
# Esta matriz armazenará os resultados da multiplicação.
for i in range(len(A)):
# Itera sobre as linhas da matriz A.
for j in range(len(B[0])):
# Itera sobre as colunas da matriz B.
for k in range(len(B)):
# Itera sobre as colunas de A e as linhas de B para calcular o produto escalar da linha i de A com a coluna j de B.
result[i][j] += A[i][k] * B[k][j]
# Realiza a multiplicação dos elementos correspondentes de A e B e soma o resultado ao elemento result[i][j].
return result
# Retorna a matriz result, que contém o produto das matrizes A e B.
# Gerar duas matrizes de tamanho grande para o teste
N = 200 # Define o tamanho N das matrizes quadradas (200x200).
A = [[i + j for j in range(N)] for i in range(N)]
# Gera a matriz A de tamanho N x N, onde cada elemento A[i][j] é a soma dos índices i e j.
B = [[i * j for j in range(N)] for i in range(N)]
# Gera a matriz B de tamanho N x N, onde cada elemento B[i][j] é o produto dos índices i e j.
start_time = time.time()
# Marca o tempo de início da multiplicação das matrizes usando a função time.time().
result = multiply_matrices(A, B)
# Chama a função multiply_matrices para multiplicar as matrizes A e B, armazenando o resultado em 'result'.
end_time = time.time()
# Marca o tempo de término da multiplicação usando a função time.time().
print(f"Tempo de execução para a multiplicação de matrizes: {end_time - start_time:.2f} segundos")
# Calcula e exibe o tempo de execução da multiplicação de matrizes, subtraindo start_time de end_time.
# O tempo é formatado para mostrar duas casas decimais.
Parte 2: Implementação em C++¶
Compile e execute o código em C++. Compare o tempo de execução com o resultado obtido na Parte 1. Observe como C++ lida com operações computacionalmente intensivas de forma mais eficiente.
Tip
Se precisar de ajuda para instalar um compilador em C++ ou compilar e executar códigos em c++, consulte o material disponível.
#include <iostream> // Inclui a biblioteca padrão de entrada e saída do C++ (necessária para usar std::cout).
#include <vector> // Inclui a biblioteca de vetores da STL (Standard Template Library) do C++, usada para criar matrizes dinâmicas.
#include <chrono> // Inclui a biblioteca de medição de tempo do C++ (necessária para medir o tempo de execução).
// Função para multiplicar duas matrizes A e B, armazenando o resultado na matriz 'result'.
void multiply_matrices(const std::vector<std::vector<int>>& A, const std::vector<std::vector<int>>& B, std::vector<std::vector<int>>& result) {
// Loop para iterar sobre as linhas da matriz A.
for (size_t i = 0; i < A.size(); ++i) {
// Loop para iterar sobre as colunas da matriz B.
for (size_t j = 0; j < B[0].size(); ++j) {
result[i][j] = 0; // Inicializa o elemento result[i][j] com 0 antes de somar os produtos.
// Loop para calcular o produto escalar da linha i de A com a coluna j de B.
for (size_t k = 0; k < B.size(); ++k) {
result[i][j] += A[i][k] * B[k][j]; // Realiza a multiplicação e soma dos elementos correspondentes de A e B.
}
}
}
}
// Função principal do programa.
int main() {
size_t N = 200; // Define o tamanho N das matrizes quadradas (200x200).
// Declara e inicializa a matriz A como uma matriz NxN preenchida com zeros.
std::vector<std::vector<int>> A(N, std::vector<int>(N));
// Declara e inicializa a matriz B como uma matriz NxN preenchida com zeros.
std::vector<std::vector<int>> B(N, std::vector<int>(N));
// Declara e inicializa a matriz result como uma matriz NxN preenchida com zeros, que armazenará o resultado da multiplicação.
std::vector<std::vector<int>> result(N, std::vector<int>(N));
// Loop para preencher as matrizes A e B com valores.
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < N; ++j) {
A[i][j] = i + j; // Preenche a matriz A com a soma dos índices i e j.
B[i][j] = i * j; // Preenche a matriz B com o produto dos índices i e j.
}
}
// Marca o tempo de início da multiplicação das matrizes usando o relógio de alta resolução.
auto start = std::chrono::high_resolution_clock::now();
// Chama a função multiply_matrices para multiplicar as matrizes A e B, armazenando o resultado em 'result'.
multiply_matrices(A, B, result);
// Marca o tempo de término da multiplicação.
auto end = std::chrono::high_resolution_clock::now();
// Calcula a duração da multiplicação subtraindo o tempo de início do tempo de término, armazenando o resultado em 'duration'.
std::chrono::duration<double> duration = end - start;
// Exibe o tempo de execução da multiplicação de matrizes no console.
std::cout << "Tempo de execução para a multiplicação de matrizes em C++: " << duration.count() << " segundos" << std::endl;
return 0; // Retorna 0, indicando que o programa foi executado com sucesso.
}
Parte 3: Paralelismo em C++¶
Compile e execute o código modificado usando o comando '-fopenmp' para ter suporte ao paralelismo. Observe a diferença no tempo de execução em comparação com as versões anteriores.
g++ -fopenmp meu_programa.cpp -o meu_executavel_paralelo
Warning
Lembre-se! Sempre que fizer alterações no seu código em c++, é necessário gerar um novo binário.
#include <iostream> // Inclui a biblioteca padrão de entrada e saída, usada para funções como std::cout.
#include <vector> // Inclui a biblioteca de vetores da STL (Standard Template Library) do C++.
#include <chrono> // Inclui a biblioteca para medição de tempo, utilizada para calcular a duração de execução.
#include <omp.h> // Inclui a biblioteca OpenMP, usada para paralelismo em C++.
void multiply_matrices(const std::vector<std::vector<int>>& A, const std::vector<std::vector<int>>& B, std::vector<std::vector<int>>& result) {
// Define a função que realiza a multiplicação de duas matrizes A e B, armazenando o resultado na matriz 'result'.
#pragma omp parallel for
// Diretiva OpenMP que paraleliza o loop 'for' que segue. Cada iteração do loop externo será executada em paralelo.
for (size_t i = 0; i < A.size(); ++i) {
// Itera sobre as linhas da matriz A. 'size_t' é um tipo de dado usado para representar tamanhos e índices.
for (size_t j = 0; j < B[0].size(); ++j) {
// Itera sobre as colunas da matriz B.
result[i][j] = 0;
// Inicializa o elemento [i][j] da matriz result com 0 antes de somar os produtos.
for (size_t k = 0; k < B.size(); ++k) {
// Itera sobre as colunas de A e as linhas de B para calcular o produto escalar da linha i de A com a coluna j de B.
result[i][j] += A[i][k] * B[k][j];
// Realiza a multiplicação dos elementos correspondentes de A e B, somando o resultado ao elemento result[i][j].
}
}
}
}
int main() {
size_t N = 200;
// Define o tamanho N das matrizes quadradas (200x200).
std::vector<std::vector<int>> A(N, std::vector<int>(N));
// Declara e inicializa a matriz A como uma matriz NxN preenchida com zeros.
std::vector<std::vector<int>> B(N, std::vector<int>(N));
// Declara e inicializa a matriz B como uma matriz NxN preenchida com zeros.
std::vector<std::vector<int>> result(N, std::vector<int>(N));
// Declara e inicializa a matriz result como uma matriz NxN preenchida com zeros, que armazenará o resultado da multiplicação.
for (size_t i = 0; i < N; ++i) {
// Itera sobre as linhas da matriz A e B.
for (size_t j = 0; j < N; ++j) {
// Itera sobre as colunas da matriz A e B.
A[i][j] = i + j;
// Preenche a matriz A com valores como a soma dos índices i e j.
B[i][j] = i * j;
// Preenche a matriz B com valores como o produto dos índices i e j.
}
}
auto start = std::chrono::high_resolution_clock::now();
// Marca o tempo de início da multiplicação das matrizes usando o relógio de alta resolução.
multiply_matrices(A, B, result);
// Chama a função multiply_matrices para multiplicar as matrizes A e B, armazenando o resultado em 'result'.
auto end = std::chrono::high_resolution_clock::now();
// Marca o tempo de término da multiplicação.
std::chrono::duration<double> duration = end - start;
// Calcula a duração da multiplicação subtraindo o tempo de início do tempo de término, armazenando o resultado em 'duration'.
std::cout << "Tempo de execução para a multiplicação de matrizes em C++ com OpenMP: " << duration.count() << " segundos" << std::endl;
// Exibe o tempo de execução da multiplicação de matrizes no console.
return 0;
// Retorna 0, indicando que o programa foi executado com sucesso.
}
Esta atividade demonstrou como a escolha da linguagem de programação pode impactar no desempenho de operações computacionalmente intensivas, como a multiplicação de matrizes. Você viu como Python e C++ lidam com essas operações e como o paralelismo pode ser uma boa opção, dependendo da complexidade do problema. Na próxima atividade, você levará essas implementações para o cluster Franky e essas diferenças ficarão ainda mais visíveis.
Tip
Se quiser saber um pouco mais sobre HPC, relembrar conceitos ou aprofundar um pouco em C++
Entrega - Atividade 01¶
-
Aumente o tamanho das matrizes nos 3 exemplos para 300x300, 900x900 e 1300x1300
-
Elabore um gráfico que relaciona a complexidade do problema (tamanho da matriz) com o tempo de execução para cada implementação.
-
Faça uma análise comparativa sobre o impacto do paralelismo no desempenho de acordo com a complexidade do problema.
-
Comente sobre como você acha que este problema pode ser abordado em um ambiente de HPC.
Submeta seu relatório até as 23h59 de 07/02 pelo Classroom, disponível no Blackboard.