Skip to content

Commit a846800

Browse files
committed
aula 04
1 parent 480af96 commit a846800

File tree

4 files changed

+274
-3
lines changed

4 files changed

+274
-3
lines changed

material/aulas/aula04/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Ao final desta atividade, você será capaz de:
66
* Analisar **heurísticas** com aleatoriedade para reduzir o espaço de busca e fugir de mínimos locais.
77
* Usar **aleatoriedade** para guiar a busca de soluções.
88

9+
!!! warning
10+
[Clique aqui para ter acesso ao relatório completo do Emil](https://github.com/emil-freme/supercomp/blob/main/Benchmark_Compiladores_HPC_E_Blocking.ipynb)
911

1012
## Um pouco de teoria: O que é Nonce?
1113

material/aulas/aula04/slides.pdf

74.4 KB
Binary file not shown.

material/aulas/aula05/index.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Paralelismo em CPU com OpenMP
2+
3+
## Objetivo
4+
5+
* **Paralelismo em CPU**: como dividir o trabalho entre múltiplos *cores*.
6+
* **Threads**: cada thread executa uma parte do trabalho.
7+
* **OpenMP**: diretivas simples em C++ para paralelizar loops e seções de código.
8+
* **Scheduling**: forma como as iterações do loop são distribuídas entre threads (`static`, `dynamic`, `guided`).
9+
10+
## Esqueleto do Código `miner_omp.cpp`
11+
12+
```cpp
13+
#include <iostream>
14+
#include <string>
15+
#include <random>
16+
#include <chrono>
17+
#include <iomanip>
18+
#include <sstream>
19+
#include <functional>
20+
#include <climits>
21+
#include <omp.h> // OpenMP
22+
23+
// =============================================================
24+
// Hash: converte string -> 64 bits -> hex
25+
// =============================================================
26+
static std::string hash_simples_hex(const std::string& input) {
27+
std::hash<std::string> hasher;
28+
unsigned long long v = static_cast<unsigned long long>(hasher(input));
29+
std::ostringstream os;
30+
os << std::hex << std::nouppercase << std::setfill('0') << std::setw(16) << v;
31+
return os.str(); // ~16 hex chars (64 bits)
32+
}
33+
34+
// =============================================================
35+
// Critério de dificuldade: hash deve começar com N zeros
36+
// =============================================================
37+
static bool validaHash(const std::string& h, int dificuldade) {
38+
if (dificuldade <= 0) return true;
39+
if ((int)h.size() < dificuldade) return false;
40+
for (int i = 0; i < dificuldade; ++i) if (h[i] != '0') return false;
41+
return true;
42+
}
43+
44+
// =============================================================
45+
// Baseline SEQUENCIAL (linear): testa nonce = 0..limite-1
46+
// =============================================================
47+
static void minerar_linear_seq(const std::string& bloco,
48+
int dificuldade,
49+
unsigned long long limite) {
50+
auto t0 = std::chrono::high_resolution_clock::now();
51+
52+
unsigned long long vencedor_nonce = 0;
53+
std::string vencedor_hash;
54+
bool found = false;
55+
56+
for (unsigned long long nonce = 0; nonce < limite; ++nonce) {
57+
const std::string tentativa = bloco + std::to_string(nonce);
58+
const std::string h = hash_simples_hex(tentativa);
59+
if (validaHash(h, dificuldade)) {
60+
found = true;
61+
vencedor_nonce = nonce;
62+
vencedor_hash = h;
63+
break;
64+
}
65+
}
66+
67+
auto t1 = std::chrono::high_resolution_clock::now();
68+
double secs = std::chrono::duration<double>(t1 - t0).count();
69+
70+
std::cout << "[SEQ-LINEAR] dif=" << dificuldade
71+
<< " | limite=" << limite
72+
<< " | tempo=" << secs << "s";
73+
if (found) {
74+
std::cout << " | nonce=" << vencedor_nonce
75+
<< " | hash=" << vencedor_hash << "\n";
76+
} else {
77+
std::cout << " | (nao encontrou)\n";
78+
}
79+
}
80+
81+
// =============================================================
82+
// ESQUELETO PARALELO (linear) — OpenMP
83+
// Objetivo: paralelizar o for abaixo.
84+
// =============================================================
85+
static void minerar_linear_omp_skel(const std::string& bloco,
86+
int dificuldade,
87+
unsigned long long limite) {
88+
auto t0 = std::chrono::high_resolution_clock::now();
89+
90+
bool found = false; // compartilhado
91+
unsigned long long vencedor_nonce = 0; // compartilhado
92+
std::string vencedor_hash; // compartilhado
93+
94+
// TODO: inserir diretiva OpenMP aqui:
95+
for (unsigned long long nonce = 0; nonce < limite; ++nonce) {
96+
if (found) continue;
97+
98+
// variáveis locais (privadas por padrão no loop)
99+
const std::string tentativa = bloco + std::to_string(nonce);
100+
const std::string h = hash_simples_hex(tentativa);
101+
102+
if (validaHash(h, dificuldade)) {
103+
// registrar um possível vencedor
104+
found = true;
105+
vencedor_nonce = nonce;
106+
vencedor_hash = h;
107+
break;
108+
}
109+
}
110+
111+
auto t1 = std::chrono::high_resolution_clock::now();
112+
double secs = std::chrono::duration<double>(t1 - t0).count();
113+
114+
std::cout << "[OMP-LINEAR] dif=" << dificuldade
115+
<< " | limite=" << limite
116+
<< " | tempo=" << secs << "s";
117+
if (found) {
118+
std::cout << " | nonce=" << vencedor_nonce
119+
<< " | hash=" << vencedor_hash << "\n";
120+
} else {
121+
std::cout << " | (nao encontrou)\n";
122+
}
123+
}
124+
125+
// =============================================================
126+
// Baseline SEQUENCIAL (random+heurística):
127+
// - Gera nonce aleatório 64-bit por tentativa (uniforme)
128+
// - Pré-filtro simples: exige primeiro char '0' antes de validar tudo
129+
// =============================================================
130+
static void minerar_random_heuristica_seq(const std::string& bloco,
131+
int dificuldade,
132+
unsigned long long tentativas) {
133+
auto t0 = std::chrono::high_resolution_clock::now();
134+
135+
std::random_device rd;
136+
std::mt19937_64 gen(rd());
137+
std::uniform_int_distribution<unsigned long long> distrib(0, ULLONG_MAX);
138+
139+
bool found = false;
140+
unsigned long long vencedor_nonce = 0;
141+
std::string vencedor_hash;
142+
143+
for (unsigned long long i = 0; i < tentativas; ++i) {
144+
const unsigned long long nonce = distrib(gen);
145+
const std::string tentativa = bloco + std::to_string(nonce);
146+
const std::string h = hash_simples_hex(tentativa);
147+
148+
// Heurística barata (pré-filtro):
149+
if (h[0] != '0') continue;
150+
151+
if (validaHash(h, dificuldade)) {
152+
found = true;
153+
vencedor_nonce = nonce;
154+
vencedor_hash = h;
155+
break;
156+
}
157+
}
158+
159+
auto t1 = std::chrono::high_resolution_clock::now();
160+
double secs = std::chrono::duration<double>(t1 - t0).count();
161+
162+
std::cout << "[SEQ-RANDH] dif=" << dificuldade
163+
<< " | tent=" << tentativas
164+
<< " | tempo=" << secs << "s";
165+
if (found) {
166+
std::cout << " | nonce=" << vencedor_nonce
167+
<< " | hash=" << vencedor_hash << "\n";
168+
} else {
169+
std::cout << " | (nao encontrou)\n";
170+
}
171+
}
172+
173+
// =============================================================
174+
// ESQUELETO PARALELO (random+heurística) — OpenMP
175+
// Objetivo: paralelizar o "lote de tentativas" por thread.
176+
// =============================================================
177+
static void minerar_random_heuristica_omp_skel(const std::string& bloco,
178+
int dificuldade,
179+
unsigned long long tentativas_total) {
180+
auto t0 = std::chrono::high_resolution_clock::now();
181+
182+
bool found = false; // compartilhado
183+
unsigned long long vencedor_nonce = 0; // compartilhado
184+
std::string vencedor_hash; // compartilhado
185+
186+
int T = omp_get_max_threads();
187+
unsigned long long quota = (tentativas_total / (T > 0 ? T : 1));
188+
189+
// TODO: criar região paralela OpenMP
190+
// TODO: obter id da thread
191+
// TODO: RNG por thread (seed distinta; ex.: rd() ^ (constante * tid))
192+
// TODO: cada thread executa sua quota de tentativas
193+
194+
auto t1 = std::chrono::high_resolution_clock::now();
195+
double secs = std::chrono::duration<double>(t1 - t0).count();
196+
197+
std::cout << "[OMP-RANDH] dif=" << dificuldade
198+
<< " | tent(TOTAL)=" << tentativas_total
199+
<< " | tempo=" << secs << "s";
200+
if (found) {
201+
std::cout << " | nonce=" << vencedor_nonce
202+
<< " | hash=" << vencedor_hash << "\n";
203+
} else {
204+
std::cout << " | (nao encontrou)\n";
205+
}
206+
}
207+
208+
// =============================================================
209+
// argv[1]=dificuldade | argv[2]=limiteLinear | argv[3]=tentativasRandom
210+
// =============================================================
211+
int main(int argc, char** argv) {
212+
const std::string bloco = "transacao_simples";
213+
const int dificuldade = (argc >= 2 ? std::stoi(argv[1]) : 5);
214+
const unsigned long long limiteLinear = (argc >= 3 ? std::stoull(argv[2]) : 500000ULL);
215+
const unsigned long long tentativasRandom = (argc >= 4 ? std::stoull(argv[3]) : 500000ULL);
216+
217+
std::cout << "=== Exercício OpenMP (Skeleton) | dif=" << dificuldade << " ===\n\n";
218+
219+
// Baselines sequenciais
220+
minerar_linear_seq(bloco, dificuldade, limiteLinear);
221+
minerar_random_heuristica_seq(bloco, dificuldade, tentativasRandom);
222+
223+
// Skeletons paralelos (para os alunos completarem)
224+
minerar_linear_omp_skel(bloco, dificuldade, limiteLinear);
225+
minerar_random_heuristica_omp_skel(bloco, dificuldade, tentativasRandom);
226+
227+
return 0;
228+
}
229+
```
230+
231+
232+
## Atividade
233+
234+
1. **Compilar o código com OpenMP**
235+
236+
```bash
237+
g++ -fopenmp miner_omp.cpp -o miner_omp
238+
```
239+
240+
2. **Rodar no cluster com SLURM** definindo o número de threads:
241+
242+
```bash
243+
srun -c 4 ./miner_parallel
244+
```
245+
246+
ou
247+
248+
```bash
249+
OMP_NUM_THREADS=8 ./miner_parallel
250+
```
251+
252+
3. **Explorar os seguintes pontos**:
253+
254+
* O que acontece quando aumentamos o número de threads?
255+
* Qual diferença entre `schedule(static)` e `schedule(dynamic)`?
256+
* O tempo de execução cai de forma linear com mais threads? Por quê?
257+
* Como a aleatoriedade influencia os resultados de cada execução?
258+
259+
---
260+
261+
## 5. Objetivo do exercício
262+
263+
Com essa prática, o aluno deve:
264+
265+
* Entender **como aplicar OpenMP** em um problema real.
266+
* Comparar desempenho entre versão sequencial e paralela.
267+
* Refletir sobre os **ganhos e limitações** do paralelismo em CPU.
268+
* Perceber que a mineração de nonces é um exemplo claro de problema "paralelizável", mas que a aleatoriedade e a sincronização (variáveis compartilhadas) afetam o resultado.
269+

mkdocs.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ nav:
3838
- "Aula 04 - Heurísticas e Aleatoriedade": aulas/aula04/index.md
3939
#- "Aula 05 - Programação paralela em CPU": aulas/aula05/index.md
4040
#- "Aula 06 - Efeitos colaterais do paralelismo": aulas/aula06/index.md
41-
#- "Aula 07 - Programação distribuída com MPI": aulas/aula07/index.md
42-
#- "Aula 08 - Programação paralela e distribuída": aulas/aula08/index.md
43-
#- "Aula 09 - Programação paralela em GPU": aulas/aula09/index.md
41+
#- "Aula 07 - Programação Distribuída": aulas/aula07/index.md
42+
#- "Aula 08 - Programação Paralela e Distribuída": aulas/aula08/index.md
43+
#- "Aula 09 - Programação Paralela em GPU": aulas/aula09/index.md
4444

4545
- Suporte:
4646
- "Aula 01" : teoria/aula01/index.md

0 commit comments

Comments
 (0)