Memória Compartilhada
Nesta seção:
A memória compartilhada é um modelo de paralelismo em que múltiplas threads (linhas de execução) compartilham o mesmo espaço de memória dentro de um único nó (servidor).
Conceitos Básicos
Threads: Múltiplas linhas de execução dentro de um mesmo processo
Memória compartilhada: Todas as threads enxergam a mesma memória
OpenMP: Padrão mais comum para programação com memória compartilhada
SMP (Symmetric Multi-Processing): Arquitetura com múltiplos processadores compartilhando memória
Características:
Todos os núcleos utilizados estão no mesmo nó
Acesso à mesma área de memória
Comunicação entre threads é muito rápida
Limitado aos recursos de um único nó
OpenMP
OpenMP é uma API para programação paralela em C/C++ e Fortran, baseada em diretivas de compilação.
Exemplo em C
/******************************************************************************
* OpenMP Example - Hello World
* O master thread cria uma região paralela.
* Todas as threads obtêm seu número único e imprimem.
******************************************************************************/
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int nthreads, tid;
/* Fork: cria um time de threads */
#pragma omp parallel private(nthreads, tid)
{
/* Obtém o número da thread */
tid = omp_get_thread_num();
printf("Hello World from thread = %d\n", tid);
/* Apenas a thread master faz isso */
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Number of threads = %d\n", nthreads);
}
} /* Join: todas as threads se encontram aqui */
return 0;
}
Compilando
No GridUnesp, recomendamos o compilador Intel, mas GCC também está disponível:
# Com Intel
module load intel/compilers
icc -openmp omp_hello.c -o omp_hello
# Com GCC
module load gcc/10.2.0
gcc -fopenmp omp_hello.c -o omp_hello
Script de Submissão
No SLURM, o número de threads é controlado pela diretiva -c (--cpus-per-task).
#!/bin/bash
#SBATCH -J openmp_test
#SBATCH -N 1 # 1 nó (obrigatório para memória compartilhada)
#SBATCH -c 8 # 8 threads
#SBATCH -t 01:00:00
#SBATCH --mem=16G
export INPUT="entrada.dat"
export OUTPUT="saida_openmp.dat"
# Define número de threads para OpenMP
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
# Carrega o módulo do compilador
module load intel/compilers
job-nanny srun ./omp_hello
Variáveis Importantes
OMP_NUM_THREADS: Número de threads a serem usadas
OMP_STACKSIZE: Tamanho da pilha por thread
OMP_SCHEDULE: Tipo de escalonamento de loops
export OMP_NUM_THREADS=28
export OMP_STACKSIZE=256M
export OMP_SCHEDULE="dynamic"
Limitações
Limitado a 1 nó (não pode usar múltiplos servidores)
Máximo de threads por nó: 56 (28 cores físicos + hyper-threading)
Máximo de threads disponível por nó: 52 (26 cores físicos + hyper-threading)
Memória total limitada à RAM do nó: 128 GB
Tipo de Nó |
Capacidade Total |
Reservado ao SO |
Disponível para Jobs |
Nós Regulares ( |
28 Cores / 56 CPUs |
2 Cores / 4 CPUs |
26 Cores / 52 CPUs |
Nó de GPU ( |
48 Cores / 96 CPUs |
4 Cores / 8 CPUs |
44 Cores / 88 CPUs |
Quando Usar Memória Compartilhada
✅ Ideal para:
Problemas que cabem na memória de um único nó
Loops pesados que podem ser paralelizados
Aplicações com muita comunicação entre threads
Quando a latência de rede seria um problema
❌ Não recomendado para:
Problemas que não cabem em 128 GB de RAM
Aplicações que precisam de mais de 52 threads
Quando o problema é naturalmente distribuído
Exemplo Prático: Produto Matricial
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#define N 1000
int main() {
double A[N][N], B[N][N], C[N][N];
double start, end;
// Inicialização
#pragma omp parallel for
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
A[i][j] = i + j;
B[i][j] = i - j;
C[i][j] = 0.0;
}
}
start = omp_get_wtime();
// Multiplicação paralela
#pragma omp parallel for
for (int i = 0; i < N; i++) {
for (int k = 0; k < N; k++) {
for (int j = 0; j < N; j++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
end = omp_get_wtime();
printf("Tempo com %d threads: %f segundos\n",
omp_get_max_threads(), end - start);
return 0;
}
Script para teste de escalabilidade:
#!/bin/bash
# Testar com 1, 2, 4, 8, 16, 28, 52 threads
for threads in 1 2 4 8 16 28 52; do
cat > job_${threads}.sh << EOF
#!/bin/bash
#SBATCH -J omp_${threads}
#SBATCH -N 1
#SBATCH -c ${threads}
#SBATCH -t 00:30:00
#SBATCH --mem=16G
#SBATCH --output=omp_${threads}_%j.out
export OMP_NUM_THREADS=${threads}
module load intel/compilers
./test_omp
EOF
sbatch job_${threads}.sh
done
Dicas de Otimização
Balanceamento de carga: Use schedule dinâmico para loops irregulares
#pragma omp parallel for schedule(dynamic, 100)Evite false sharing: Coloque variáveis compartilhadas em diferentes linhas de cache
Use variáveis privadas: Declare variáveis temporárias como privadas
#pragma omp parallel for private(temp)Nested parallelism: Cuidado ao aninhar regiões paralelas
export OMP_NESTED=FALSE
Ver também
OpenMP - Programação com memória compartilhada
SMP - Múltiplos processadores compartilhando memória
Memória Distribuída - Para problemas que não cabem em um nó
Processando Simulações - Conceitos básicos de submissão
Otimizando o Desempenho - Como encontrar a configuração ideal
Aplicações Instaladas - Exemplos com aplicações reais