Este projeto tem como objetivo principal explorar os conceitos de programação concorrente utilizando a biblioteca POSIX PThreads em C. Através da implementação de um algoritmo para contar números primos em grandes matrizes de números aleatórios, o trabalho compara o desempenho entre uma abordagem serial e uma paralela. O foco está na análise do impacto do paralelismo, da sincronização de threads via mutexes e da granularidade das tarefas (macroblocos) no tempo de execução e na correção dos resultados.
- Geração de Matriz: Criação dinâmica de uma matriz de grandes dimensões (
20000 x 20000) preenchida com números naturais aleatórios (0 a 31999). - Busca Serial de Primos: Implementação de um algoritmo sequencial para identificar e contar números primos na matriz, servindo como tempo de referência.
- Busca Paralela de Primos (PThreads): Utilização de múltiplas threads para processar a matriz em "macroblocos", distribuindo a carga de trabalho.
- Sincronização com Mutexes: Proteção de variáveis globais compartilhadas (
contadorPrimos,proximoMacrobloco) para garantir a integridade dos dados e a correção do resultado em ambiente concorrente. - Medição de Desempenho: Cálculo e exibição do tempo de execução para as buscas serial e paralela.
- Análise de Condições de Corrida: Inclusão de uma versão da busca paralela sem mutexes para demonstrar o problema da inconsistência de dados em ambiente não sincronizado.
Este projeto foi desenvolvido e testado no Microsoft Visual Studio 2022 com o ambiente de C++ Desktop Development e a biblioteca PThreads-w32.
- Microsoft Visual Studio 2022: Com a workload "Desenvolvimento para desktop com C++" instalada.
- PThreads-w32: A biblioteca foi configurada manualmente conforme as instruções do professor, copiando
pthreadVC2.libepthreadVC2.dllpara os diretórios apropriados do Visual Studio.- Certifique-se de que a macro
#define HAVE_STRUCT_TIMESPECestá definida no topo do seumain.cpara evitar conflitos de tipo.
- Certifique-se de que a macro
- Clone o Repositório:
git clone https://github.com/saraivagustavo/Trabalho-PThreads.git cd [nome-do-seu-repositorio] - Abra no Visual Studio: Abra o arquivo de solução (
.sln) do projeto no Visual Studio 2022. - Configurar Plataforma: No Visual Studio, selecione a plataforma de solução para x64 (ou x86 se preferir, mas x64 é recomendado para matrizes grandes).
- Compilar: No menu do Visual Studio, vá em
Build > Build Solution(ouCtrl+Shift+B). - Executar: Para rodar o programa, vá em
Debug > Start Without Debugging(ouCtrl+F5). O console exibirá os tempos de execução e o total de primos para as buscas serial, paralela com mutex e paralela sem mutex.
Você pode ajustar as seguintes macros no arquivo main.c para testar diferentes cenários:
#define MATRIZ_LINHAS [X]#define MATRIZ_COLUNAS [Y]#define NUM_THREADS [N](Número de threads a serem criadas)#define MACROBLOCO_LINHAS [ML]#define MACROBLOCO_COLUNAS [MC]
Os testes foram conduzidos em AMD Ryzen 7 5300X3D (8 Cores Físicos, 16 Threads Lógicas) e AMD Ryzen 5 5600 (6 Cores Físicos, 12 Threads Lógicas), utilizando matrizes de até 30000 x 30000 elementos.
- Aceleração pelo Paralelismo: A busca paralela consistentemente superou a serial em matrizes grandes quando otimizada.
- Ex: Matriz 20000 x 20000: Serial: 15.776s (Ryzen 7); Paralela (1000 x 1000 macrobloco, 16 Core): 1.650s.
- Impacto da Granularidade (Tamanho do Macrobloco):
- Macroblocos Pequenos (ex: 1 x 1): Causam overhead excessivo de sincronização, resultando em desempenho pior que o serial (ex: 68.616s para 1 x 1 no Ryzen 7). A alta frequência de aquisição/liberação de mutexes (mesmo que ausentes no teste sem mutex) e a gestão de muitas pequenas tarefas anulam os ganhos.
- Macroblocos Ótimos: Uma "faixa ideal" (aprox. 1000 x 1000 a 5000 x 5000) proporcionou os melhores tempos, balanceando a divisão do trabalho com o overhead (ex: 1.650s no Ryzen 7 para 1000 x 1000).
- Macroblocos Grandes (ex: matriz inteira): Reduzem o paralelismo efetivo, aproximando o tempo do serial (ex: 15.81$s para 20000 x 20000 no Ryzen 7), pois o trabalho não é distribuído de forma eficiente entre as threads.
- Influência do Número de Threads/Cores:
- O desempenho melhora com o aumento do número de threads até o limite de núcleos físicos/lógicos, conforme a Lei de Amdahl.
- Ex: Matriz 20000 x 20000 (Ryzen 7, 1000 x 1000 macrobloco): de 8.003s (2 Core) para 1.650s (16 Core).
- O uso de threads lógicas (Hyper-Threading) pode oferecer ganhos, mas com menor eficiência do que os núcleos físicos.
- Necessidade da Sincronização (Mutexes):
- Testes sem mutexes, embora não tenham manifestado inconsistência na contagem de primos em algumas execuções (devido à natureza não determinística das condições de corrida e otimizações), são teórica e pragmaticamente cruciais para demonstrar que a proteção de variáveis compartilhadas é vital para a correção e consistência dos resultados em um ambiente multithread. Sem mutexes, o risco de resultados incorretos devido a condições de corrida é sempre presente.
| Aluno | GitHub | |
|---|---|---|
| Gustavo Saraiva Mariano | ||
| Pedro Henrique Albani Nunes | ||
| Tertuliano dos Santos Junior |
- Disciplina: Sistemas Operacionais
- Professor: Flávio Giraldeli
- Instituição: INSTITUTO FEDERAL DO ESPÍRITO SANTO - Campus Serra