目标
UPC++ 并行化基因组组装
一句话总结:使用 UPC++ 并行化 de novo 基因组组装——实现分布式哈希表来存储和遍历 k-mer,从 reads 中重建 contigs。修改 kmer_hash.cpp 和 hash_map.hpp,在 1-8 节点、每节点 60 任务上测试扩展性。
这是你第一次用 PGAS 模型编程。与 MPI 的 two-sided 通信不同,UPC++ 的 rget/rput 让你可以直接读写远程 rank 的内存——无需远端 CPU 参与,硬件 RDMA 让通信与计算重叠。
| 组件 | 对应课程 | 核心概念 |
|---|---|---|
| kmer_hash.cpp | Lecture 11 | 分布式哈希表、k-mer 存储 |
| hash_map.hpp | Lecture 11 | PGAS 数据结构、global_ptr |
| contig 生成 | Lecture 11 | k-mer 遍历、图遍历 |
| 多节点扩展 | Lecture 11 | 弱扩展 / 强扩展实验 |
| 报告 | — | 扩展图 + 优化讨论 |
upcxx::init() / upcxx::finalize() 初始化和清理upcxx::rank_me() / upcxx::rank_n() 获取当前 rank 和总 rank 数upcxx::new_<T>() / upcxx::delete_<T>() 分布式内存分配upcxx::global_ptr<T> 全局指针,包含 affinity 信息upcxx::rget() / upcxx::rput() 异步 one-sided 操作upcxx::barrier() 全局同步先理解串行版本的基因组组装流程:
数据集:/global/cfs/cdirs/mp309/cs267-spr2025/hw3-datasets/human-chr14-synthetic.txt(51-mers)
核心挑战:k-mer 分布在多个 rank 上,如何高效存取?
rput 写到远端,查询时用 rget 读取关键约束:不能通过 RPC 调用本地 std::unordered_map。这看似简单,但违背了 PGAS 的设计意图——每个请求都要触发远程调用,性能极差。
正确做法:在 UPC++ 的框架下直接操作分布式内存,利用 rget/rput 的异步性和批量能力。
哈希表构建完成后,需要遍历 k-mer 图生成 contigs:
设计两组实验:
绘制扩展图(speedup vs rank 数),分析扩展效率。
平凡解陷阱: 用 UPC++ 的 RPC 调用本地 std::unordered_map 是最容易走的路,但性能极差且违反题目要求。评委会检查你的实现方式。
同步过度: 每次 rget/rput 都等待完成会丧失异步优势。应该批量发起操作,最后统一 wait。
数据倾斜: 基因组中某些 k-mer 出现频率极高(如重复序列)。简单的哈希分区可能导致某些 rank 负载过重。
忘记 barrier: UPC++ 的异步操作需要显式同步。在关键阶段后忘记 upcxx::barrier() 会导致数据竞争。
内存泄漏: upcxx::new_ 分配的内存必须用 upcxx::delete_ 释放。分布式内存泄漏比本地更隐蔽且更致命。
// rank 0 读取 rank 1 上的数据
upcxx::global_ptr<int> remote = ptr; // rank 1 上的指针
int value;
upcxx::future<> fut = upcxx::rget(remote, &value, sizeof(int));
// ... 做其他工作 ...
fut.wait(); // 等待读取完成
// 批量 rput:聚合多次写入为一次通信
std::vector<upcxx::global_ptr<int>> dst_ptrs;
std::vector<int> src_values;
// ... 填充数据 ...
upcxx::future<> fut = upcxx::rput(
src_values.data(), dst_ptrs.data(), sizeof(int) * dst_ptrs.size()
);
fut.wait();
// 在所有 rank 上分配一个对象
upcxx::global_ptr<int> ptr = upcxx::new_<int>(0);
// 检查 affinity(该指针指向哪个 rank)
int owner = ptr.where();
// 仅在拥有者 rank 上操作
if (owner == upcxx::rank_me()) {
*ptr.local() = 42; // 转为本地指针访问
}
HW3 是 PGAS 模型的实战演练。UPC++ 的思想在后续课程中继续延伸:
卡住时的正确顺序: 复习 Lecture 11 → 读 UPC++ 文档 → 从单机单 rank 开始调试 → 逐步增加 rank 数 → 分析通信瓶颈。
问题 1:UPC++ 的 global_ptr<T> 包含哪些信息?
问题 2:为什么 UPC++ 的 rget/rput 比 MPI 的 MPI_Send/MPI_Recv 更适合不规则通信?
问题 3:为什么 HW3 禁止通过 RPC 调用本地 std::unordered_map?
问题 4:在 UPC++ 中,为什么批量操作(batching)对性能至关重要?
问题 5:在基因组组装中,"k-mer"是什么?
UPC++ 并行化基因组组装
分布式哈希表 + one-sided 通信
禁止 RPC 平凡解
扩展图 + 优化讨论 + 实现描述