Parallel Computing - 一文讲懂并行计算

2024-03-02 21:52

本文主要是介绍Parallel Computing - 一文讲懂并行计算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • Throughput/Latency
  • Serial Computing
  • Parallel Computing
  • Types of parallel computers
  • Simple 4-width SIMD
  • Amdahl's law
  • Types of parallelism
    • **Data Parallel Model**
    • Task parallel
  • Partitioning
    • Domain Decomposition
    • Functional Decomposition
  • Communications
    • Example that does not need communication
    • Example that need communication
  • Synchronization
    • Barrier
  • Granularity
    • Fine-grain Parallelism
    • Coarse-grain Parallelism
  • How expensive is memory I/O
  • Solving the problem
    • Just add more stuff for the GPU to do
    • Latency Hiding
  • Coalesced Global memory access
  • Host/Device Transfers and Data Movement
    • Avoid transfers
    • Pinned Host Memory

在本章中,我们将回顾一些有关并行计算的概念。但更加强调 GPU。

Throughput/Latency

在讨论性能之前,我们先回顾一下一些概念。

  • 吞吐量:单位时间内计算任务的数量。即:一分钟内 1000 笔信用卡付款。
  • 延迟:调用操作和获得响应之间的延迟。即:处理信用卡交易所需的最长时间为 25 毫秒。
    在优化性能时,一个因素(例如吞吐量)的改进可能会导致另一因素(例如延迟)的恶化。

Serial Computing

这是老办法,我们遇到一个问题,我们把它们分解成一个个小块,然后一个接一个地解决。
在这里插入图片描述

Parallel Computing

从最简单的意义上来说,并行计算是同时使用多个计算资源来解决计算问题。
在这里插入图片描述

Types of parallel computers

根据弗林分类法,并行计算机有 4 种不同的分类方法。
下面是一些经典的例子

  • SISD:非常旧的计算机(PDP1)
  • MIMD:超级计算机
  • SIMD:Intel 处理器、Nvidia Gpus
  • MISD:确实很少见。
    对于 GPU,它们通常是 SIMD 类型的处理器。不同的处理单元执行相同的指令,但在共享内存的不同部分。
    在这里插入图片描述

Simple 4-width SIMD

下面我们有一个 4 宽度的 SIMD。这里的所有处理器都在同时执行“add”指令。
在这里插入图片描述当您听说 GPU 有 5000 个核心时,请不要被愚弄,它可能只是说它有 5000 个 ALU(算术逻辑单元)。 GPU 可同时执行的最大任务数通常在 Nvidia 上称为“warp size”,在 AMD 上称为“wavefront”,通常是按块/网格组织的 32 宽 SIMD 单元。

可能发生的一个有趣的问题是,如果您有一条分支(if)指令,并且每个处理元素决定不同的事情。如果发生这种情况,您将受到处理处罚。这种效应称为发散。为了解决这个问题,您必须尝试尽量减少波动前(cuda 中的wrap)上分支指令的使用。
如果您需要这种分支分配,您可以使用 opencl 中的“select”来编译为单个指令(原子),这样就不会发生发散问题。
在这里插入图片描述

Amdahl’s law

Amdahl’s Law指出潜在的程序加速(理论延迟)由可以并行化的代码 p 的比例定义:
在这里插入图片描述

  • S : 整个任务执行延迟的理论加速
  • p: 可以并行化的代码的一部分。
  • 处理器数量
    从该定律可以得到:加速受到不可并行工作部分的限制,即使使用无限数量的处理器,速度也不会提高,因为串行部分会受到限制。
    在这里插入图片描述
    程序的总执行时间T分为两类:
  • 执行不可并行串行工作所花费的时间
  • 进行可并行工作所花费的时间
    这里还缺少一些重要的东西。阿姆达尔定律没有考虑内存延迟等其他因素。

Types of parallelism

Data Parallel Model

在此模型上,共享内存对所有节点都是可见的,但每个节点都处理该共享内存的部分内容。这就是我们通常使用 GPU 要做的事情
在这里插入图片描述
数据并行方法的主要特点是编程相对简单,因为多个处理器都运行相同的程序,并且所有处理器大约在同一时间完成其任务。当每个处理器正在处理的数据之间的依赖性最小时,此方法是有效的。例如,向量加法可以从这种方法中受益匪浅。

Task parallel

任务并行方法的主要特点是每个处理器执行不同的命令。与数据并行方法相比,这增加了编程难度。由于处理时间可能会根据任务的分割方式而有所不同,因此需要一些同步。如果任务完全不相关,问题就会容易得多。
在这里插入图片描述

Partitioning

设计并行程序的第一步是将问题分解为可以分配给多个任务的离散工作“块”。这称为分解或划分。有两种在并行任务之间划分计算工作的基本方法:

  • 域分解:
  • 功能分解。

Domain Decomposition

在这种类型的分区中,与问题相关的数据被分解。然后,每个并行任务都处理一部分数据。
在这里插入图片描述

Functional Decomposition

在这种方法中,重点是要执行的计算,而不是计算所操纵的数据。问题根据必须完成的工作进行分解。然后,每个任务执行整体工作的一部分。
在这里插入图片描述

Communications

通常一些并行问题需要节点(任务)之间进行通信。这又是一个与问题相关的问题。需要考虑的一些要点:

  • 通信总是意味着开销
  • 通信频繁需要节点(任务)同步,需要较大的开销
    当您需要将数据发送到 GPU 来执行某些计算,然后将结果传回 CPU 时,就意味着需要进行通信。

Example that does not need communication

某些类型的问题可以分解并并行执行,几乎不需要任务共享数据。例如,想象一下图像处理操作,其中黑白图像中的每个像素都需要反转其颜色。图像数据可以轻松地分配给多个任务,然后这些任务彼此独立地完成各自的工作。这些类型的问题通常被称为“尴尬并行”,因为它们非常简单。需要很少的任务间通信。

Example that need communication

大多数并行应用程序并不是那么简单,并且确实需要任务彼此共享数据。例如,3-D 热扩散问题需要任务了解具有相邻数据的任务计算出的温度。相邻数据的更改会直接影响该任务的数据。

Synchronization

管理工作顺序和执行工作的任务是大多数并行程序的关键设计考虑因素。同步总是会影响性能,但当任务需要通信时总是需要同步。
同步类型

  • Barrier(用于 OpenCl)
  • Lock/semaphore 锁/信号量
  • Synchronous communication operations 同步通讯操作

Barrier

这是一种同步机制,每个任务都执行其工作,直到到达屏障。然后它会停止或“阻塞”,直到所有任务都到达同一点。当最后一个任务到达屏障时,所有任务都会同步。

Granularity

这是关于计算和通信之间的比率。有 2 种粒度

Fine-grain Parallelism

通信多于计算
在这里插入图片描述

Coarse-grain Parallelism

计算多于沟通
在这里插入图片描述
最有效的粒度取决于算法及其运行的硬件环境。但是……通常通信的延迟比计算的延迟更大。例如,将数据复制到 GPU 或从 GPU 复制数据。所以我们更喜欢粗粒度,这意味着大量的计算和很少的 GPU/CPU 通信。

How expensive is memory I/O

正如心理实验一样,想象一个处理元素(节点/任务)在 1 秒内做出语句(即 V:=1+2+3/4)。但如果需要读/写 GPU 全局内存,则需要更多时间。考虑下表。
顺便说一句,我们考虑到数据已经在 GPU 上,将数据发送到 GPU 是另一个问题。
在这里插入图片描述在此表中,我们有不同的内存类型,其中全局内存是 GPU 内存,私有内存和本地内存是位于每个核心内部的内存,常量也是全局内存,但专门用于读取速度更快。现在检查以下示例。
在这里插入图片描述
在这种情况下,我们的计算结果是 整个时间的 1 86 \frac{1}{86} 861 。这很糟糕,这意味着我们的 ALU 工作在 A L U e f i c i e n c y ALU_{eficiency} ALUeficiency= 1 86 ∗ 100 \frac{1}{86}*100 861100 A L U e f i c i e n c y ALU_{eficiency} ALUeficiency=1.1%。
现在想象一下,我们需要处理更多的数据,而不是 int(4 字节)x,y 将是 long(8 字节)。
在这里插入图片描述
现在情况变得更糟了。我们的计算结果是整个时间的 1 172 \frac{1}{172} 1721 。这很糟糕,这意味着我们的 ALU 工作在 A L U e f i c i e n c y ALU_{eficiency} ALUeficiency= 1 172 ∗ 100 \frac{1}{172}*100 1721100 A L U e f i c i e n c y ALU_{eficiency} ALUeficiency=0.58%。

如果我们想与某些原始顺序算法相比提高性能,这可能意味着两件事:

  • 原来的顺序算法一定比这个内存I/O延迟慢很多。
  • 您需要在 GPU 内执行更多操作才能稀释该时间。

Solving the problem

Just add more stuff for the GPU to do

我们可能想到的第一件事是添加更多要完成的处理,这实际上会花费比内存延迟更多的时间。同样,如果处理时间加上内存延迟小于原始顺序 CPU 版本,您将获得加速。
在这里插入图片描述在这种情况下,您现在拥有 100% 的 Alu 效率,但这仅在现实生活中当您处理令人尴尬的并行问题时才会发生。例如大矩阵乘法、密码分析等…

Latency Hiding

更好的技术是使用 GPU 上下文切换机制来隐藏此延迟。这是通过向 GPU 发出并行代码标志来实现的,表明它正在等待可用的内存请求。当这种情况发生时,等待可用内存的处理元素组将进入池。与此同时,GPU 可以启动另一个工作组来执行,但最终会暂时停止。这个想法是,当这种情况发生时,一些工作项将具有可用的内存,这将具有最小化整个延迟的效果。
在这里插入图片描述
所以我们用工作组(work group)来溢出我们的计算单元
在这里插入图片描述
然后,我们隐藏内核访问全局内存的长内存延迟时间,因为当 GPU 分配要执行的工作组时,有些工作组可能是可用的。顺便说一句,当您的工作组位于波前(或warp)内时,这将起作用.
在这里插入图片描述

Coalesced Global memory access

主机向GPU发送数据后,内存将位于全局内存上,每个线程(计算单元)都会访问数据。我们已经讨论过这很慢,但有时您需要这样做。每次内核在全局内存上读/写时,它实际上是在访问一块内存。合并访问是指访问相邻地址上的数据。
在这里插入图片描述
因此,这意味着使用较少的线程消耗相邻内存块来访问内存比使用大量的线程消耗随机地址更快。

Host/Device Transfers and Data Movement

到目前为止,我们仅考虑数据已位于 GPU(全局内存)上时的性能。这忽略了 GPU 编程中最慢的部分,即从 GPU 获取数据和从 GPU 取出数据。
在这里插入图片描述
我们不应该仅使用内核的 GPU 执行时间相对于其 CPU 实现的执行时间来决定是运行 GPU 还是 CPU 版本。当我们最初将代码移植到 GPU 时,我们还需要考虑通过 PCI-e 总线移动数据的成本。
因为 GPU 是插入 PCI-e 总线的,所以这很大程度上取决于 PCI 总线的速度以及有多少其他东西正在使用它。

host/device transfer latency 是尝试在 GPU 上加速算法时的主要困难。

发生这种情况是因为,如果您的顺序算法计算的时间小于此主机/设备传输的时间,则无需做太多事情。但有一些…

Avoid transfers

这是最明显的一个,但您至少需要一个,对吗?因此,宁愿进行一次大传输,也不愿进行多次小传输,特别是在程序循环中。

Pinned Host Memory

默认情况下,主机 (CPU) 数据分配是可分页的。 GPU 无法直接从可分页主机内存访问数据,因此当调用从可分页主机内存到设备内存的数据传输时。发生这种情况是因为操作系统为其所有设备提供了虚拟地址,并且您的驱动程序需要使用这些页面来获取真实地址。 GPU 驱动程序必须首先分配一个临时页面锁定或“固定”主机数组,将主机数据复制到固定数组,然后将数据从固定数组传输到设备内存,如下所示。
在这里插入图片描述
如图所示,固定内存用作从设备到主机传输的暂存区域。我们可以通过直接在固定内存中分配主机阵列来避免可分页和固定主机阵列之间的传输成本。
您不应该过度分配固定内存。这样做会降低整体系统性能,因为它会减少操作系统和其他程序可用的物理内存量。很难提前判断多少才算是太多,因此与所有优化一样,测试您的应用程序及其运行的系统以获得最佳性能参数。

这篇关于Parallel Computing - 一文讲懂并行计算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/767538

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

一文全面详解Python变量作用域

《一文全面详解Python变量作用域》变量作用域是Python中非常重要的概念,它决定了在哪里可以访问变量,下面我将用通俗易懂的方式,结合代码示例和图表,带你全面了解Python变量作用域,需要的朋友... 目录一、什么是变量作用域?二、python的四种作用域作用域查找顺序图示三、各作用域详解1. 局部作

一文彻底搞懂Java 中的 SPI 是什么

《一文彻底搞懂Java中的SPI是什么》:本文主要介绍Java中的SPI是什么,本篇文章将通过经典题目、实战解析和面试官视角,帮助你从容应对“SPI”相关问题,赢得技术面试的加分项,需要的朋... 目录一、面试主题概述二、高频面试题汇总三、重点题目详解✅ 面试题1:Java 的 SPI 是什么?如何实现一个

一文详解PostgreSQL复制参数

《一文详解PostgreSQL复制参数》PostgreSQL作为一款功能强大的开源关系型数据库,其复制功能对于构建高可用性系统至关重要,本文给大家详细介绍了PostgreSQL的复制参数,需要的朋友可... 目录一、复制参数基础概念二、核心复制参数深度解析1. max_wal_seChina编程nders:WAL

一文详解如何查看本地MySQL的安装路径

《一文详解如何查看本地MySQL的安装路径》本地安装MySQL对于初学者或者开发人员来说是一项基础技能,但在安装过程中可能会遇到各种问题,:本文主要介绍如何查看本地MySQL安装路径的相关资料,需... 目录1. 如何查看本地mysql的安装路径1.1. 方法1:通过查询本地服务1.2. 方法2:通过MyS

一文详解如何在Vue3中封装API请求

《一文详解如何在Vue3中封装API请求》在现代前端开发中,API请求是不可避免的一部分,尤其是与后端交互时,下面我们来看看如何在Vue3项目中封装API请求,让你在实现功能时更加高效吧... 目录为什么要封装API请求1. vue 3项目结构2. 安装axIOS3. 创建API封装模块4. 封装API请求

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-