About HPC

Applications

HPC Programming Models

Memory Sharing & Sync

  • Shared Memory Model: (OpenMP, pThreads, …)
    • 传统的物理上共享的内存区域较难实现,常用于单节点多核
    • SHMEM: Symmetric Hierarchical MEMory
      • 用于并行计算系统的单边通信库
      • 是一种 PGAS 的实现
      • 通过库函数直接访问对称内存区域中的远程数据,而不需要显式消息传递
      • 对称结构完全一致,运行时可以根据本地指针推断远程指针地址,适合 DP
      • OpenSHMEM
    • DSM: Distributed Shared Memory
      • 物理上是分布式内存,但通过软件或硬件提供全局共享内存视图
      • 底层会处理一致性和通信
      • Implementations: TreadMarks, Ivy…
  • Distributed Memory Model: Message Passing (MPI, …)
    • Message Passing Interface
    • 显式地通过 sendreceive 等函数在进程间传递消息
  • PGAS: Partitioned Global Address Space
    • 一种编程模型,把整个系统的内存看作一个全局地址空间,但将其 分区 到不同进程/线程
    • 每个进程只能访问自己的部分,但是也可以通过某种方式访问远程的内存
    • 既能方便访问远程数据,又能优化数据局部性
    • 内存模型中包含“本地”和“远程”部分,程序员对数据分布有显式控制
    • UPC (Unified Parallel C), Coarray Fortran, Chapel, OpenSHMEM
  • Hybrid: MPI+OpenMP, MPI+CUDA

MPI

MPI

  • 进程间通信
    • OpenMPI
    • MPICH
    • MVAPICH
    • Intel MPI / IBM MPI / Microsoft MPI / …
  • Objects
    • Processes: Independent execution units, separate mem space.
    • Communicators: Groups of procs that can communicate
    • Ranks: Unique ID for procs within a communicator
  • Communication Patterns
    • P2P (Pair)
      • Blocking: MPI_Send, MPI_Recv
      • Non-Blocking: MPI_Isend, MPI_Irecv
        • Ensure sent: MPI_Wait, MPI_Test
    • Collective (Group)
      • Broadcast: MPI_Bcast
      • Gather/Scatter ==(from/to root)==: MPI_Gather, MPI_Scater
      • Reduction: MPI_Reduce (to root), MPI_Allreduce (to all)
    • One-Sided / Remote Memory Access
      • MPI_Put, MPI_Get
      • 窗口:在使用单边通信之前,进程必须明确地将一部分内存注册为一个“窗口”,允许其他进程对其进行单边访问。操作依据窗口的指定位置。
      • 需要单独的同步机制(MPI_Win_fence, MPI_Win_lock/MPI_Win_unlock, etc)来确保数据一致性和操作的完成
#include <mpi.h>
#include <stdio.h>
 
int main(int argc, char** argv) {
    // Initialize MPI environment
    MPI_Init(&argc, &argv); 
 
    // Get total number of processes
    int world_size;
    MPI_Comm_size(MPI_COMM_WORLD, &world_size); 
 
    // Get the rank of the process
    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); 
 
    // Print a message from each processor
    printf("Hello from processor %d out of %d processors\n", world_rank, world_size);
 
    // Finalize the MPI environment
    MPI_Finalize(); 
 
    return 0;
}
// Random Order
Hello from processor 0 out of 4 processors
Hello from processor 1 out of 4 processors
Hello from processor 2 out of 4 processors
Hello from processor 3 out of 4 processors
  • MPI_COMM_WORLD: Communicator
指向原始笔记的链接

PGAS

  • Illusion:抽象的全局的地址空间,逻辑上被所有进程分区
  • Each process has affinity to a portion of the shared memory, enabling both local and remote memory accesses.(调优的时候注意优化亲和性)
  • Feature
    • 结合了共享内存编程的简便性与消息传递的性能
    • 支持单边通信
    • SPMD: Single Program Multiple Data/多数据执行
    • Data Locality Awareness
  • Implementations
    • Lang-based: UPC (C), CAF (Fortran), X10, Chapel
    • Lib-based: OpenSHMEM, Global Arrays
#include <upc.h>
 
shared int A[THREADS];
 
int main() {
    A[MYTHREAD] = MYTHREAD;
    upc_barrier;
    printf("Thread %d sees A[0] = %d\n", MYTHREAD, A[0]);
    return 0;
}
Thread 0 sees A[0] = 0
Thread 1 sees A[0] = 0
Thread 2 sees A[0] = 0
Thread 3 sees A[0] = 0
  • shared: 逻辑上对所有 UPC 线程是可见的
  • THREADS: 总的线程数(或进程数)

Shared Memory Model

  • 多个处理器或线程访问一个公共的内存空间
  • 直接读取和写入共享变量
  • Features
    • Global Address Space: 所有线程共享相同的内存地址空间
    • Implicit Communication: 数据交换通过共享变量发生
    • Thread-Based Parallelism: 通常在一个单一进程内使用线程来实现
  • 架构实现
    • SMP (Symmetric Multiprocessing): 所有 CPU 核心通过一个统一的总线或其他互连机制连接到共享内存。每个处理器访问任何内存地址的延迟和带宽大致相同。
    • NUMA (Non-Uniform Memory Access): 处理器被组织成多个“NUMA节点”,每个节点都有自己的本地内存控制器和内存。
  • 编程实现
    • OpenMP, Pthreads
  • 挑战
    • Sync: Race condition & Deadlocks
    • Scalability: 更多的线程不一定会得到更好的性能
    • Memory Consistency
    • Debugging difficulty
  • 最佳实践
    • 适当地使用同步原语
    • 尽可能减少对共享数据的访问/传递:减少/私有化无用成员
    • 线程安全:细粒度锁、尽量多不变性、倾向原子操作、封装

Hybrid

  • 结合了多种并行编程模型(异构:CPU, GPU, Network)
    • MPI: inter-node (distributed memory)
    • OpenMP: intra-node (shared memory)
    • CUDA/OpenACC: accelerators like GPUs
  • 常见
    • MPI + OpenMP - CPU Multi-cores
    • MPI + Pthreads - Fine-grained
    • MPI + CUDA/OpenACC - CPU + GPU
    • MPI + PGAS - PGAS within nodes, MPI across
  • 挑战
    • 理解和管理不同模型之间的交互、数据传输和同步
    • 将程序的并行结构(例如 MPI 进程的数量和 OpenMP 线程的数量)与底层硬件的拓扑结构(如节点数量、每个节点的核心数、缓存层次、NUMA 域)精确匹配
    • 调试和性能分析在混合编程环境中变得更加困难
    • 线程安全(尤其是 MPI_THREAD_MULTIPLE
  • 最佳实践
    • Coarse-grained MPI + fine-grained OpenMP
      • MPI: 大的、独立的计算任务块
      • OpenMP:并行化节点内的细粒度循环或计算
    • MPI process per NUMA domain
      • 避免跨 NUMA 域的昂贵内存访问
    • Avoid over-subscription
    • Benchmark different configurations (MPI ranks vs threads)
  • Examples
    • Climate models (气候模型): 模拟地球气候系统,涉及大量的分布式计算和节点内部的并行化。
    • Fluid dynamics (流体力学): 模拟流体行为,如空气动力学、水流等,通常需要大规模并行计算。
    • Deep learning frameworks (深度学习框架): 如 TensorFlow、PyTorch 等,在训练大型神经网络时,经常在分布式集群中使用 MPI 进行模型或数据并行,并在每个节点内部使用线程(通常是 GPU 上的 CUDA 线程)进行高效的矩阵运算。

CUDA

CUDA 是 NVIDIA 的并行计算平台和编程模型,它使开发人员能够利用 GPU 的海量计算能力(大规模并行)。

关键概念:

  • 数十万个线程:
    • 专为细粒度并行设计。GPU 并发运行大量线程。
  • SIMT 执行模型:
    • 线程被分组到 warps 中,以同步(单指令,多线程)方式执行。
  • 分层内存:
    • 全局内存: 所有线程可访问。
    • 共享内存: 快速的、按块(block)划分的内存。
    • 局部内存: 每个线程私有。
  • 编程语言:
    • 主要使用带扩展的 C/C++,并通过封装器(wrappers)支持 Python、Fortran 和其他语言。

Utils

nvidia-smi topo 分析 GPU 之间的互联结构

Assets

  • NVIDIA HPC SDK
  • DALI 是一个加载数据的加速模块
  • Magnum IO 加速数据中心 IO 性能
    • 用 NCCL 替换 MPI 可以显著提升 VASP 的性能。UCX 可以加速 VASP、Chroma、MIA-AI、Fun3d、CP2K 和 Spec-HPC2021 等科学计算应用,从而缩短运行时间。