hpc-lab-code/lab3/prime/BOTTLENECK_ANALYSIS.md
2026-01-21 18:02:30 +08:00

262 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prime Number MPI Program - Bottleneck and Scalability Analysis
## 程序瓶颈分析
### 1. **算法瓶颈:低效的素数检测算法**
**问题:** 程序使用最简单的试除法检测素数,时间复杂度为 O(n²)
```cpp
for ( j = 2; j < i; j++ ) // 对每个数字i需要检查i-2次
{
if ( i % j == 0 )
{
prime = 0;
break;
}
}
```
**影响:**
- 检测数字 2需要 0 次除法
- 检测数字 100,000需要 99,998 次除法
- 检测数字 1,000,000需要 999,998 次除法
**改进建议:**
- 只检查到 √i 而不是 i-1可将复杂度降至 O(n√n)
- 使用埃拉托斯特尼筛法Sieve of Eratosthenes
- 使用更高效的算法如米勒-拉宾素性测试
---
### 2. **负载均衡瓶颈:进程间计算成本不均**
**问题表现:**
从性能测试结果可以看到:
| N值 | 进程数 | 时间(秒) | 加速比 | 效率 |
|-------|--------|----------|--------|--------|
| 100K | 1 | 1.23 | 1.00x | 100% |
| 100K | 2 | 1.32 | 0.96x | 48% |
| 100K | 4 | 0.67 | 1.88x | 47% |
| 100K | 6 | 0.68 | 1.85x | 30% |
| 100K | 8 | 0.37 | 3.38x | 42% |
**关键问题:**
- 2个进程时加速比 < 1比单进程还慢
- 4个进程时加速比仅 1.88x理想应该是 4x
- 6个进程时效率仅 30%理想应该是 100%
- 8个进程时效率仅 42%
**根本原因:**
虽然程序使用循环分配策略让各进程检查相近数量的数字
```
P=4时
- 进程0: 2, 6, 10, 14, ..., 99998 (25000个数字)
- 进程1: 3, 7, 11, 15, ..., 99999 (25000个数字)
- 进程2: 4, 8, 12, 16, ..., 100000 (25000个数字)
- 进程3: 5, 9, 13, 17, ..., 99997 (24999个数字)
```
**但是!** 数字大小不同检测成本差异巨大
- 进程0检测的数字2, 6, 10, 14, ... (小数字检测快)
- 进程3检测的数字5, 9, 13, 17, ... (大数字检测慢)
**计算成本分析:**
虽然各进程检查的数字数量相近
- 检测小数字如2, 3, 4只需要很少的除法运算
- 检测大数字如99997, 99998, 99999需要大量除法运算
这导致
- **进程0**检测的数字最小总计算成本最低
- **进程P-1**检测的数字最大总计算成本最高
**实际负载分布N=100000, P=4**
```
进程0: 检测 [2, 6, 10, ..., 99998] → 平均数字大小 ≈ 50000
进程1: 检测 [3, 7, 11, ..., 99999] → 平均数字大小 ≈ 50001
进程2: 检测 [4, 8, 12, ..., 100000] → 平均数字大小 ≈ 50002
进程3: 检测 [5, 9, 13, ..., 99997] → 平均数字大小 ≈ 50001
```
虽然平均数字大小相近但大数字的检测成本远高于小数字
---
### 3. **通信瓶颈MPI_Reduce的开销**
**问题:** 每个进程计算完成后需要调用 `MPI_Reduce` 汇总结果
```cpp
MPI_Reduce(&total_part, &total, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
```
**影响:**
- 当进程数增加时通信延迟增加
- 对于小规模问题如N=100000通信开销占比显著
---
### 4. **同步瓶颈:进程间相互等待**
**问题:** 由于负载不均衡快的进程需要等待慢的进程完成
**表现:**
- 进程0检测小数字很快完成
- 进程P-1检测大数字很慢才完成
- 所有进程必须等待最慢的进程完成才能调用 MPI_Reduce
---
## 加速比问题分析
### 问题12个进程时加速比 < 1
**现象:** 使用2个进程比单进程还慢
**原因:**
1. **通信开销 > 并行收益**当N=100000时问题规模较小MPI通信和同步的开销超过了并行计算的收益
2. **负载不均衡**2个进程时进程0检测偶数位置数字进程1检测奇数位置数字但奇数位置的平均数字更大检测成本更高
3. **缓存效应**单进程可能有更好的缓存局部性
### 问题2效率随进程数增加而下降
**现象:**
- 4进程效率 47%
- 6进程效率 30%
- 8进程效率 42%
**原因:**
1. **Amdahl定律**程序中存在串行部分MPI初始化Reduce汇总结果打印限制了最大加速比
2. **通信开销增加**进程数越多通信和同步开销越大
3. **负载不均衡加剧**进程数越多进程间的计算成本差异越明显
### 问题36进程效率异常低30%
**可能原因:**
1. **NUMA效应**6个进程可能跨越不同的CPU socket导致跨socket通信开销增加
2. **线程调度**操作系统调度6个进程到不同核心可能产生额外的上下文切换开销
3. **内存带宽竞争**6个进程同时访问内存可能导致带宽饱和
---
## 改进建议
### 1. **改进素数检测算法**
```cpp
// 改进只检查到√i
int is_prime(int n) {
if (n < 2) return 0;
if (n == 2) return 1;
if (n % 2 == 0) return 0;
for (int j = 3; j * j <= n; j += 2) {
if (n % j == 0) return 0;
}
return 1;
}
```
**预期效果:** 将算法复杂度从 O(n²) 降至 O(nn)可提速约 n
### 2. **改进负载均衡策略**
**方案A块分配Block Distribution**
```cpp
// 将数字范围分成P个连续的块
int block_size = (n - 1) / p;
int start = 2 + id * block_size;
int end = (id == p - 1) ? n : 2 + (id + 1) * block_size - 1;
for (int i = start; i <= end; i++) {
// 检测i是否为素数
}
```
**优点:** 每个进程处理连续的数字范围减少缓存失效
**缺点:** 仍然存在负载不均衡后面的进程处理更大的数字
**方案B动态负载均衡**
```cpp
// 使用任务队列,进程完成一个任务后领取下一个
int current = 2;
#pragma omp critical
{
current = next_number++;
}
if (current <= n) {
// 检测current是否为素数
}
```
**优点:** 自动实现负载均衡
**缺点:** 需要同步机制可能增加开销
**方案C反向分配**
```cpp
// 让进程0处理大数字进程P-1处理小数字
for (int i = n - id; i >= 2; i -= p) {
// 检测i是否为素数
}
```
**优点:** 简单部分缓解负载不均衡
**缺点:** 不能完全解决问题
### 3. **减少通信开销**
```cpp
// 使用非阻塞通信
MPI_Ireduce(&total_part, &total, 1, MPI_INT, MPI_SUM, 0,
MPI_COMM_WORLD, &request);
// 在等待通信完成的同时做其他工作
MPI_Wait(&request, MPI_STATUS_IGNORE);
```
### 4. **优化数据局部性**
```cpp
// 预分配缓存,避免频繁分配
int* primes = (int*)malloc((n - 1) * sizeof(int));
int prime_count = 0;
// 批量处理,提高缓存命中率
for (int i = start; i <= end; i++) {
if (is_prime(i)) {
primes[prime_count++] = i;
}
}
```
---
## 总结
### 主要瓶颈:
1. **算法瓶颈**O(n²)的素数检测算法效率低下
2. **负载均衡瓶颈**进程间计算成本严重不均
3. **通信瓶颈**MPI_Reduce的同步开销
4. **同步瓶颈**快进程等待慢进程
### 加速比问题:
1. **小规模问题**通信开销 > 并行收益
2. **负载不均衡**:导致效率随进程数增加而下降
3. **Amdahl定律**:串行部分限制了最大加速比
### 优先改进项:
1. **改进算法**将试除法优化到√n最优先
2. **改进负载分配**:使用块分配或动态分配
3. **减少通信**:使用非阻塞通信或减少通信频率
通过这些改进,预期可以将加速比从当前的 3.38x8进程提升到接近理想的 6-7x。