Lecture 39: GPU Architecture


image-20240815131730989


GPU与CPU架构的对比

image-20240815132250434

  • GPU(图形处理单元)和 CPU(中央处理单元)虽然在架构上存在一些相似之处,但它们的设计目的和应用场景截然不同。

  • CPU 通常配备较少的核心(通常为2至8个),每个核心都具备复杂的控制逻辑和较高的执行性能。CPU的设计目标是最小化处理延迟,以高效处理复杂的串行任务,如操作系统的任务调度、程序的逻辑运算和其他依赖顺序执行的任务。

  • GPU 则拥有大量的执行单元(通常超过100个),控制逻辑相对简单,但核心数量非常多。GPU的设计目的是最大化并行处理的吞吐量,适用于需要同时处理大量数据的任务,特别是在图形渲染、机器学习和科学计算等领域。

GPU的并行处理能力

  • GPU的架构允许大量的执行单元同时并行工作,因此在处理需要大量相似或重复计算任务时,GPU表现得尤为出色。典型应用包括图形渲染、视频处理、深度学习模型训练等。

  • 在处理图像或视频时,GPU可以将屏幕上的像素数据分配给不同的执行单元进行并行处理。例如,每个像素的颜色计算可以同时在不同的执行单元中进行,从而显著提高处理速度和效率。这种并行处理的能力使得GPU在实时图形渲染、视频编码/解码、科学计算等领域具有明显的优势。

GPU内部的可编程性

  • 图形管线是GPU执行图形任务的核心流程,包括从3D模型的顶点处理到最终2D图像显示的一系列步骤。GPU的图形管线包括多个阶段,如顶点着色、几何处理、光栅化和像素着色等。每个阶段都可以通过GPU的并行处理能力来加速。

  • GPU内部的可编程性是其一大优势。现代GPU允许开发者通过编写专门的着色器程序(如顶点着色器、片段着色器)来优化特定任务的处理效率。例如,开发者可以编写自定义的着色器代码来调整图形渲染效果,或通过计算着色器来处理复杂的物理模拟或人工智能推理任务。

  • 这种可编程性不仅限于图形渲染,GPU的通用计算能力(GPGPU)使其能够用于广泛的非图形计算任务,如密码学、数据分析、机器学习等领域。

  • 后面会通过实际编程示例和演示,可以直观地展示如何利用GPU的并行处理能力进行高效的编程。GPU编程通常涉及到大量的并行计算,例如对大规模数据进行同时处理。这种并行计算能力在处理图形任务时尤为有效,尤其是在需要在屏幕上同时移动和处理大量像素的情况下。GPU能够在极短的时间内处理数百万像素,从而实现高帧率的实时图形渲染。

性能优化的重点

  • 对于CPU,性能优化的关键是最小化从执行单元到内存的延迟。CPU的瓶颈通常在于缓存未命中、分支预测失败以及指令流水线的停顿。因此,CPU的优化策略包括改进缓存命中率、减少内存访问延迟以及优化指令流水线。

  • 对于GPU,优化的重点则是最大化执行单元的吞吐量,确保尽可能多的执行单元在并行工作。GPU的瓶颈可能在于线程调度、内存带宽限制或执行单元的利用率不足。因此,GPU的优化策略包括平衡负载、减少内存带宽消耗、提高线程并发度以及优化指令调度和 分发。

CPU VS GPU

image-20240815132609213

核心数量(Cores)

  • CPU:CPU核心数量相对较少,一般为2到16个核心,但每个核心的计算能力强大,适合处理复杂的单线程任务。每个核心具备独立的控制逻辑,能够执行不同的指令流,因此CPU擅长处理需要高计算能力和灵活性的任务,如操作系统管理、复杂的逻辑运算等。

  • GPU:相比之下,GPU拥有大量核心,通常有几百到上千个。这些核心相对简单,设计上更注重并行处理的能力,因此非常适合处理需要同时处理大量数据的任务,如图形渲染、大规模矩阵计算等。GPU通过并行执行大规模相同类型的计算任务,显著提高了整体处理速度。

频率(Frequency)

  • CPU:CPU通常具有较高的时钟频率,过去CPU性能的提升很大程度上依赖于时钟频率的提高。高频率使CPU能够在单位时间内执行更多的指令,从而提高任务的响应速度。CPU频率的提升直接影响了延迟敏感任务的处理效率。

  • GPU:GPU的时钟频率通常低于CPU,但其设计更关注吞吐量(即单位时间内处理的数据量)。GPU的多核心架构使其能够在较低的频率下,通过并行处理大量任务来实现高性能。因此,GPU的效率主要体现在处理大规模数据的并行计算中,而不是单一核心的计算速度。

延迟(Latency)

  • CPU:CPU的设计极为关注低延迟,以确保指令能够快速被执行。这使得CPU在处理需要快速响应的任务时表现优异,如交互式应用程序、实时操作系统任务等。低延迟是CPU在单线程任务中保持高效的关键。

  • GPU:相对于CPU,GPU的延迟较高,但通过大量的并行执行单元来弥补这一缺陷。在处理需要高吞吐量的任务时,GPU的并行能力可以显著减少整体处理时间,即使单个任务的延迟较高。GPU在需要处理大量相似数据的情况下依然非常高效,如图形处理和深度学习推理。

并行性(Parallelism)

  • CPU:CPU主要通过指令级别的并行性(Instruction-Level Parallelism, ILP)来优化任务执行效率。它利用流水线技术、超标量技术等,通过在同一时间内执行多条指令来加速任务处理。这种并行性对于处理复杂、依赖性强的任务非常有效。

  • GPU:GPU在线程级别实现并行,数千个线程可以同时运行。它擅长处理数据并行的任务,即大量相同类型的操作需要在不同的数据集上同时执行。GPU的这种大规模并行处理能力在图形渲染、科学计算和人工智能训练中尤为突出。

寄存器(Registers)

  • CPU:CPU中的寄存器数量相对较少,但通过寄存器重命名技术,CPU能够提高执行效率,减少指令间的依赖性问题。寄存器的高速访问使得CPU可以快速处理任务,并在需要时快速切换上下文。

  • GPU:GPU拥有大量寄存器,以支持其大规模并行计算的需求。每个执行单元都有自己的寄存器文件,确保并行任务之间能够快速存取数据。这些寄存器对于GPU保持高效并行处理至关重要,因为它减少了对较慢存储器的访问需求。

推测执行(Speculation)

  • CPU:CPU高度依赖推测执行(如分支预测)来优化指令执行顺序,减少处理器的空转时间。推测执行使CPU能够提前执行可能需要的指令,从而在实际指令流确定后减少延迟。这种复杂的推测执行机制大大提高了CPU在复杂任务中的执行效率。

  • GPU:由于GPU的执行单元众多,推测执行的复杂性会极大增加设计难度,因此GPU通常不采用分支预测。相反,GPU倾向于使用简单的顺序执行方式,这样可以简化控制逻辑,更好地适应其大量执行单元的设计特点。

执行顺序(Execution Order)

  • CPU:CPU支持乱序执行,通过重排序缓冲区(Reorder Buffers)来提高指令执行的性能。乱序执行使得CPU能够动态地安排指令执行顺序,从而优化处理器资源的利用率,减少等待时间,提升任务执行速度。

  • GPU:GPU通常采用顺序执行的方式。尽管顺序执行看似简单,但对于GPU而言,这种设计能够简化控制逻辑,并最大化执行单元的利用效率,特别是在大规模并行计算任务中。

执行单元(Execution Units)

  • CPU:CPU的执行单元数量相对较少,但每个单元功能强大,适合处理复杂的指令。这些执行单元能够完成复杂的算术逻辑运算、浮点运算以及内存访问操作,因此CPU可以在单一任务中发挥出色的性能。

  • GPU:GPU的执行单元数量众多,但每个单元设计相对简单。GPU通过大量执行单元同时处理简单的计算任务,显著提高了整体吞吐量。这使得GPU在处理大量相似计算任务时能够非常高效,如大规模矩阵运算、图形渲染等。

执行控制(Execution Control)

  • CPU:CPU的控制逻辑复杂,设计上追求优化指令执行的效率,最大限度地利用处理器资源。复杂的控制逻辑使得CPU能够灵活处理各种任务,特别是在多任务处理环境中。

  • GPU:GPU的控制逻辑相对简单,主要依赖软件层面的优化和编程模型来管理并行任务。GPU通过简化控制逻辑来支持其大规模并行架构,从而提高数据处理的整体效率。

一致性(Coherency)

  • CPU:CPU通常由硬件管理数据一致性,特别是在多核处理器中,确保每个核心看到的内存数据是一致的。这种硬件级别的数据一致性管理对于保证多核处理器的正确性和性能至关重要。

  • GPU:在GPU中,数据一致性通常由软件管理,开发者需要自行处理线程间的数据同步。虽然这增加了编程的复杂性,但也赋予了开发者更大的灵活性,能够在不同的并行计算模型中优化性能。

类比:教授与学生的数学问题

  • 类比说明:一个有趣的类比将CPU比作一位数学教授,而GPU则类似一间教室里的小学生。

    • CPU作为教授:教授擅长解决复杂的问题,具有高度的分析和推理能力。但由于只有一个教授,即使他/她再聪明,也难以在短时间内解决大量简单的数学问题。

    • GPU作为学生们:教室里的小学生可能没有教授那样的能力,但如果让他们解决大量简单的数学问题,他们可以在极短的时间内通过并行工作解决所有问题。因此,在处理大量简单、重复性任务时,GPU的表现要远优于CPU。

  • 总结:即使教授(CPU)再聪明,也无法与100个同时进行简单运算的小学生(GPU)竞争。这类比很好地说明了CPU和GPU在处理不同类型任务时的优势和适用场景:复杂的单一任务适合由CPU处理,而大量简单并行任务则是GPU的强项。

最大化吞吐量与隐藏延迟

吞吐量(Throughput)

在GPU架构中,最大化吞吐量是关键目标。这意味着GPU的设计旨在充分利用其众多执行单元,以同时处理大量数据。与CPU不同,CPU通常侧重于低延迟执行单一或少量任务,而GPU则旨在通过并行处理多个任务来在短时间内完成大量计算任务。这种设计使得GPU在处理图形渲染、大规模科学计算和人工智能训练时尤为高效,因为这些任务通常涉及处理大量的相同或相似数据。

隐藏延迟(Latency Hiding)

为了提高整体处理效率,GPU通过管理多个线程的执行来隐藏延迟。具体来说,当某些线程因等待内存数据或其他资源而暂停时,GPU可以立即调度其他线程执行任务。这种多线程并行执行的机制确保了GPU执行单元始终在工作,从而有效地隐藏了延迟,提高了系统的整体吞吐量。延迟隐藏是GPU能够在面对高延迟的存储访问时,仍然保持高性能的关键。

SIMD(单指令多数据流)

SIMD的概念

SIMD(Single Instruction Multiple Data)是并行编程中的重要概念,指一条指令可以同时作用于多个数据元素。GPU广泛采用SIMD架构,使其能够在单个时钟周期内对多个数据进行处理。对于图形处理,SIMD的一个典型应用是对屏幕上每个像素同时执行相同的指令,这体现了线程级别的并行性(Thread-Level Parallelism)。每个线程负责处理一个或多个像素的数据,所有线程同时执行相同的指令,从而显著提高图像处理效率。

SIMD在GPU中的应用

在GPU中,SIMD架构通常用于并行处理图像中的每个像素或3D模型中的每个顶点。这种并行处理能力使得GPU在渲染图像、执行物理模拟和处理视频编码时表现得尤为出色。通过将工作负载分配给不同的线程组,GPU能够显著提高计算效率,尤其是在处理需要高并行度的数据任务时。

内存带宽与数据传输

高内存带宽

GPU通常拥有比CPU更高的内存带宽,这是因为GPU需要频繁刷新屏幕,并在极短时间内处理大量像素数据。高内存带宽确保了GPU能够迅速访问和处理大量数据,避免成为瓶颈。在图形渲染和游戏中,GPU必须每秒刷新屏幕30次或更多次,每次刷新涉及数百万像素的渲染和处理。高内存带宽是确保这些计算任务能够实时进行的重要因素。

数据传输优化

由于GPU需要频繁地在芯片内外传输数据,因此优化内存带宽使用至关重要。为此,GPU设计了缓存层次结构(Cache Hierarchies),以减少数据在片上存储器(On-Chip Memory)和片外存储器(Off-Chip Memory)之间的传输量。通过使用多级缓存,GPU可以有效地减少对主内存的访问次数,从而提高数据访问速度。这种缓存结构不仅提升了数据传输效率,还降低了内存延迟对整体系统性能的影响。

图形处理流水线

下面是简化后的图形处理流水线(Graphics Pipeline),它展示了图形数据从顶点处理到最终呈现在屏幕上的完整过程。以下是对图片中各个步骤的详细讲解:

image-20240815141126692

1. 顶点处理(Vertex Processing)

  • 顶点:图像的最基本单位,通常表示物体的角点。在这个阶段,顶点被输入到图形处理流水线中。
  • 顶点着色(Vertex Shading):在这个步骤中,每个顶点会执行一个着色程序,这些程序通常会进行坐标变换,将顶点从模型空间转换到屏幕空间,同时应用一些其他的效果如光照。
  • 统一着色器核心(Unified Shader Core):图中左侧的绿色部分表示的是统一着色器核心,这意味着无论是顶点着色还是片段着色,都可以在相同的硬件上执行,从而提高了硬件资源的利用率。

2. 光栅化(Rasterization)

  • 光栅化:这个步骤将处理后的顶点数据转换为屏幕上的像素点。具体来说,它将顶点形成的几何图元(如三角形)转化为由像素构成的图像。
  • 图元(Primitives):这里显示的是三角形图元,它们是图形学中最常见的基本构建块。光栅化的目的是将这些图元分解成栅格上对应的像素位置。

3. 片段处理(Fragment Processing)

  • 片段(Fragments):光栅化后,每个像素称为一个片段。在片段处理阶段,GPU可以对每个片段执行片段着色器(Fragment Shader),以计算它的最终颜色值。
  • 片段着色(Fragment Shading):这个阶段可以应用纹理、光照和其他效果,以生成最终的像素颜色。

4. 帧缓冲区(Framebuffer)

  • 帧缓冲区:这是存储最终像素数据的内存区域。在完成片段处理后,像素数据被写入帧缓冲区,然后显示在屏幕上。
  • 像素(Pixels):帧缓冲区中的每个片段数据对应一个屏幕上的像素。

5. 简化图形流水线的整体流程

  • 顶点数据输入:从顶点处理开始,输入的是顶点的位置信息。
  • 几何图元转换为像素:通过光栅化,将几何图元转换为像素。
  • 最终图像的生成:片段处理阶段为每个像素赋予颜色,并将这些像素写入帧缓冲区,形成最终显示的图像。

流水线图表明了图像渲染从顶点到像素的处理过程,并展示了GPU在不同阶段如何处理和转换数据。图形处理的每一个阶段都对应特定的硬件功能,从顶点着色到最终像素的显示,都在统一的着色器核心上执行,这使得图形处理更加灵活和高效。这也是现代GPU能够高效处理复杂图形渲染任务的关键所在。

image-20240815141052891

这张图片详细展示了一个基于 Metal 图形 API 的简化图形处理流水线,展示了从 3D 世界的几何数据到屏幕上显示的像素图像的整个过程。以下是对图片中各个步骤的详细讲解:

1. 顶点处理(Vertex Processing)

  • 顶点变换(Vertex Transformations):在这个阶段,输入的 3D 模型的顶点数据通过变换矩阵(如世界坐标、视图矩阵、投影矩阵等)被转换到屏幕空间的 2D 坐标系统,同时保留深度信息(Depth)。
  • 可编程着色器(Programmable Shader):在这个阶段,开发者可以使用 Metal 着色语言编写自定义的顶点着色器,以实现特定的顶点处理效果。
  • 这个阶段主要是将 3D 顶点数据转换为可以在屏幕上渲染的 2D 数据。

2. 裁剪与图元组装(Clipping/Primitive Assembly)

  • 裁剪(Clipping):这个步骤会移除那些在视野之外或者在屏幕边界之外的几何图元,确保只处理实际需要显示的部分。
  • 图元剔除(Culling):这个步骤通常用于移除背面朝向摄像机的三角形或者不可见的部分,以减少不必要的渲染计算。
  • 图元组装(Primitive Assembly):在这个阶段,经过裁剪的顶点被重新组合成三角形等几何图元,为后续的光栅化做准备。

3. 光栅化(Rasterization)

  • 扫描转换(Scan Conversion):光栅化是将三角形等图元转换为屏幕上的像素点的过程。具体来说,这个步骤将每个图元的2D数据映射到屏幕的像素网格上。
  • 在光栅化过程中,会判断哪些像素位于图元内部,只有这些像素才会被进一步处理和渲染。

4. 片段处理(Fragment Processing)

  • 光照(Lighting):在这个阶段,针对每个像素进行光照计算,以模拟真实的光照效果。
  • 纹理映射(Texturing):从内存中提取纹理数据并将其应用到像素上,增加图像表面的细节和真实感。
  • 效果滤镜(Effect Filters):在生成最终像素颜色之前,可以应用各种滤镜效果,如模糊、锐化等。
  • 可编程着色器(Programmable Shader):开发者可以使用 Metal 着色语言编写自定义的片段着色器,以实现特定的片段处理效果。
  • 这个阶段最终生成每个像素的颜色值,决定它在屏幕上显示的颜色。

5. 合并/输出(Merge/Output)

  • Alpha 混合(Alpha Blending):处理透明像素的合成,将多个图层的颜色混合在一起。
  • 深度测试(Z-Testing):确保前景物体覆盖背景物体,通过比较深度值(Z 值)来决定哪个像素应该被绘制。
  • 这个阶段将处理后的像素数据写入帧缓冲区(Framebuffer),并最终输出到显示设备上。

顶点处理(Vertex Processing)

image-20240815142513889

线框图(Wireframe)

线框图是一种将三维场景中的物体以线条和顶点的形式显示出来的方法。它将物体简化为由边和顶点组成的几何框架,展示了物体的基本结构。在图形渲染过程中,复杂的三维形状,如独角兽模型或背景中的其他几何体,通常都会被分解为更基础的三角形。这是因为三角形是最简单的平面图元,具有数学上的稳定性和易处理性,因此在硬件加速中处理三角形比处理其他复杂形状要简单得多。

三角形分解

三角形分解是图形处理中将所有几何形状(无论是简单的正方形还是复杂的曲面)最终转换为一组三角形的过程。三角形具有独特的优势,它们总是共面(在一个平面内),这使得计算变得更加简单和高效。GPU的设计基于这一特性,利用三角形来进行大部分的渲染工作,以确保图形处理的精度和速度。

顶点着色器(Vertex Shader)

变换(Transforms)

顶点着色器的主要任务是对物体的顶点数据进行变换操作,将物体正确定位在三维世界中。这些变换操作包括旋转、平移和缩放,可以使物体在三维空间中按照指定的方式移动或调整大小。顶点着色器通过对每个顶点应用这些变换,使物体在屏幕上的位置和形状符合预期。这些变换的实现通常依赖于矩阵运算,这也是GPU处理顶点数据的核心。

相机位置

顶点着色器还负责将场景中的物体变换到相机视图空间中。相机的视角决定了最终用户在屏幕上看到的内容。通过调整相机的位置和方向,开发者可以控制场景的视角,使得物体相对于相机或相机相对于物体进行移动。顶点着色器会将物体在世界坐标系中的位置变换到相机坐标系中,这一步骤是渲染图像的前提。

矩阵乘法(Matrix Multiplication)

矩阵变换

在顶点处理过程中,所有的几何变换(如旋转、平移、缩放)都可以通过4x4矩阵来表示。顶点坐标与这些变换矩阵相乘,从而将顶点从一个空间变换到另一个空间。这种矩阵乘法操作是GPU的强项,尤其是在早期的GPU中,固定功能硬件被用来加速这些运算,使得大规模的几何计算得以在实时渲染中高效执行。

预乘矩阵

为了优化计算性能,多个变换矩阵可以在实际应用之前预先相乘,形成一个复合变换矩阵。然后,这个复合矩阵可以一次性应用于顶点数据,减少了逐个应用各个变换矩阵的计算量。这样可以简化计算过程,加快顶点处理的速度,同时保持高效的图形渲染性能。

固定功能硬件与可编程着色器

固定功能硬件(Fixed-Function Hardware)

在早期的GPU设计中,顶点处理由固定功能硬件执行。开发者只能使用预定义的变换操作,这些操作直接编码到硬件中,如简单的旋转、平移和缩放。虽然这种方法在计算速度上具有优势,但灵活性较差,无法满足越来越复杂的图形处理需求。

可编程着色器(Programmable Shader)

随着图形技术的发展,现代GPU引入了可编程着色器,开发者可以通过编写自定义的顶点着色器代码来实现各种复杂的变换操作。可编程着色器使得开发者能够超越固定功能的限制,进行更复杂的图形操作,例如实现自定义光照、动画和其他高级效果。这种灵活性极大地扩展了图形处理的可能性,使现代图形渲染更加丰富和多样。

总结

顶点处理是图形渲染流水线中的关键步骤,它将三维物体的顶点数据转换为屏幕上可显示的二维坐标。通过顶点着色器,开发者可以对物体进行各种变换操作,如旋转、平移和缩放,从而使物体在三维空间中的显示位置符合预期。现代GPU通过可编程着色器取代了早期的固定功能硬件,使得顶点处理变得更加灵活和强大。GPU的矩阵乘法加速能力使其能够高效处理大量顶点数据,支持复杂三维场景的实时渲染。

几何处理(Geometry Processing)

image-20240815142733815

三角形组装(Triangle Assembly)

在几何处理阶段,顶点数据从独立的顶点集合被组装成基本的几何图元,即三角形。这是图形渲染流水线的一个关键步骤,因为大多数3D对象都是由大量的三角形组成的。通过将顶点连接成三角形,GPU能够在接下来的渲染步骤中处理这些基本单元,而不是单独的顶点。

三角形组装的过程不仅仅是简单的连接顶点,它还包括计算这些三角形在三维空间中的法线、边界信息等,这些数据对于后续的光照计算、遮蔽处理和材质应用都至关重要。通过组装三角形,几何处理为接下来的屏幕空间渲染奠定了基础。

屏幕空间中的三角形处理

屏幕空间(Screen Space)

屏幕空间是指经过顶点处理和投影变换后的二维坐标系。在这个阶段,三维的场景被转换成屏幕上的二维表示,每个顶点的位置被映射到屏幕的像素坐标系中。这一转换使得GPU能够直接处理屏幕上的像素数据,准备进行最终的渲染。

在屏幕空间中,三角形被转换为一组像素块,这些像素块表示屏幕上需要绘制的具体位置。这一阶段的处理非常重要,因为它直接决定了最终图像的分辨率和细节表现。

三角形分箱(Triangle Binning)

现代GPU为了提高渲染效率,通常会对屏幕空间中的三角形进行分箱处理。分箱是指将屏幕划分为多个小区域,每个区域包含一定数量的三角形。通过将这些区域独立处理,GPU可以更好地管理和分配渲染任务。

这种分箱处理能够有效地减少GPU的计算负担,避免一次性处理整个屏幕上的所有像素。它还可以提高缓存命中率,减少内存带宽的消耗,从而提高整体的渲染效率。

复杂度估计与任务分配

三角形复杂度(Triangle Complexity)

在三角形分箱处理之后,GPU会对每个区域内的三角形数量进行复杂度估计。三角形复杂度指的是在一个区域内需要渲染的三角形的密集程度。通过颜色表示的图示中,颜色越亮的区域表示该区域内的三角形数量越多,渲染时需要更多的计算资源。例如,复杂的几何体如独角兽的毛发部分由于包含大量的小三角形,渲染时的计算量非常大,因此在复杂度图中表现为较亮的区域。

工作负载分配(Workload Distribution)

为了确保GPU的高效利用,GPU会根据三角形的复杂度对渲染任务进行工作负载分配。复杂度高的区域意味着需要更多的计算资源,因此GPU会将这些区域分配给更多的执行单元,以实现均衡的工作负载。

这种动态的任务分配机制可以避免某些执行单元过载,而其他执行单元则闲置的情况,从而最大化GPU的吞吐量,提高整体的渲染性能。

优化渲染性能

优化的重要性

渲染优化是图形处理中的关键环节。通过分析三角形的分布和复杂度,开发者可以识别出渲染过程中可能出现的瓶颈,并采取措施加以优化。例如,对于高复杂度的区域,开发者可以优化模型的多边形数量,或者通过调整渲染顺序来减少计算开销。

这种优化不仅能够提高GPU的利用率,还能显著提升整个场景的渲染效率,减少不必要的计算浪费。最终的结果是能够在相同的硬件条件下呈现更加流畅和高质量的图像。

优化渲染性能的目标是确保GPU的每个执行单元都得到充分利用,避免资源浪费。通过精细的几何处理、有效的屏幕空间管理和合理的任务分配,GPU能够在复杂的三维场景中高效地完成渲染任务,提供出色的视觉效果。

image-20240815144152690

光栅化(Rasterization)

扫描转换(Scan Conversion)

光栅化的核心是将三角形等几何图形转换为屏幕上的单个像素,这个过程称为扫描转换。在扫描转换中,算法通常从一个顶点开始,沿着三角形的边界行走,确定边界像素的位置。然后,算法沿着水平线填充三角形内部的像素,直到整个三角形都被分解为具体的像素。这一过程是图形渲染的基础,因为它将抽象的几何形状转化为具体的像素点,供后续的渲染步骤使用。

像素生成

在扫描转换完成后,三角形的每个部分都会对应到屏幕上的具体像素。这些像素代表了最终显示图像的基础,每个像素的颜色和位置决定了整个图像的视觉表现。

抗锯齿(Anti-Aliasing)

抗锯齿技术旨在通过在图像边缘生成更多的采样点来提升图像质量。通常,在光栅化过程中,边缘的像素会产生锯齿状的效果,尤其是在低分辨率下显得尤为明显。抗锯齿通过对边缘进行多重采样,并混合这些样本的颜色值,从而使得边缘更加平滑,减少了视觉上的锯齿效果。这种技术对提升图像的视觉质量非常重要,尤其是在渲染高对比度边缘时。

image-20240815144222392

片段处理(Fragment Processing)

像素着色

在生成像素之后,下一步是决定每个像素的颜色,这一过程称为片段处理。片段处理根据多种因素为每个像素赋予颜色值,包括光照效果、表面材质、纹理映射等。这一步骤决定了图像最终的视觉效果,比如一个物体在光照下的明暗变化、纹理的细节等。

深度信息

在片段处理中,除了颜色值之外,每个像素还携带有深度信息,即Z值。深度信息用于确定像素在三维空间中的相对位置。这个Z值对于后续的深度测试至关重要,因为它帮助确定哪个像素应该显示在前景,哪个应该被遮挡。

深度测试(Depth Test)

深度测试

深度测试用于决定哪些像素应该被渲染。在三维场景中,如果两个三角形或像素在屏幕上重叠,深度测试会比较它们的深度值(Z值),并只渲染靠近观察者的像素。通过这种方式,深度测试确保了只有在前景中的物体或像素被显示,而背景中的物体则被隐藏。这不仅提高了图像的真实性,还避免了不必要的渲染计算,从而提升了性能。

清除未触及的像素

在渲染过程中,有些像素可能位于背景或者被其他物体完全遮挡,这些像素无需渲染。在深度测试之后,这些不需要渲染的像素可以被标记为“清除”状态,从而节省计算资源。通过这种方式,系统可以集中资源渲染真正可见的像素,进一步优化渲染性能。

着色(Shading)

统一着色核心(Unified Shader Core)

现代GPU采用统一的着色核心(Unified Shader Core)设计,这意味着它可以同时执行顶点、片段、以及计算着色器。这种设计极大地提升了GPU的灵活性和硬件利用率,因为相同的核心可以根据需要处理不同类型的着色任务,而不再需要为每种任务单独设计不同的硬件单元。

每像素算法(Per-Pixel Algorithm)

着色器允许开发者为每个像素指定独立的算法,例如光照计算、纹理映射、甚至是复杂的物理模拟。每像素着色技术使得渲染效果更加精细、真实。例如,通过在每个像素上应用光照算法,开发者可以控制光照如何与物体表面相互作用,从而创造出逼真的阴影、高光和反射效果。

光照计算与反射

image-20240815145141931

示例:茶壶的着色。在左边的茶壶上,只应用了简单的黑白着色,结果看起来平淡无奇。而在右边的茶壶上,应用了更复杂的镜面反射光照(Specular Lighting)。这种光照效果模拟了光线在物体表面的反射,使茶壶看起来更加有立体感和真实感。例如,在茶壶的把手、顶部和壶嘴部分,我们可以看到光线根据观察角度的变化产生的反射高光,这大大提升了视觉效果的真实度。

光照计算的复杂性

光照计算涉及多个复杂的操作,如计算光线与物体表面的角度、执行纹理查找,以及应用高光反射等。这些计算需要大量的资源,特别是当每个像素都需要进行独立的光照计算时。这种像素级别的精细计算虽然昂贵,但却是生成高质量图像的关键。

并行计算的优势

并行计算

GPU的设计天然适合并行计算,特别是在执行着色任务时。通过SIMD(单指令多数据)算法,GPU能够同时对大量像素执行相同的着色程序。这种并行处理能力使得GPU在处理图形渲染任务时效率极高,远远超过传统CPU逐个像素进行计算的速度。例如,在复杂场景中,GPU可以在短时间内计算数百万像素的光照和纹理,使得实时渲染成为可能。

Metal着色语言

image-20240815145251659

在现代渲染系统中,如Apple的Metal API,着色程序使用专门为其硬件设计的语言编写。一个典型的Metal着色程序可能会执行如下操作:计算光照角度、进行点积运算、查找纹理颜色,然后将纹理颜色与光照角度的计算结果相乘,最终生成每个像素的颜色。这种流程不仅确保了高效的光照计算,还可以通过Metal语言的优化进一步提升性能。

现代着色语言的进化

复杂性增加

随着计算需求的增加,现代着色语言变得越来越复杂,能够处理更多的计算任务。例如,现在的着色语言不仅支持基础的矩阵乘法,还集成了用于深度学习的神经网络专用指令,以及多种并行处理的优化技术。这些进步使得开发者可以在GPU上运行更复杂的算法,推动了实时渲染、机器学习和数据并行计算的发展。

同步与条件执行

现代着色语言还增加了对线程同步和条件执行的支持。这些功能对于确保多线程环境下的数据一致性非常重要。例如,通过引入if/then语句,开发者可以在着色程序中实现条件分支,这使得程序能够根据不同的场景或数据状态进行动态调整,从而优化并行性能并避免不必要的计算。

image-20240815145931150

执行模型(Execution Model)

片段着色器并行执行

在GPU中,片段着色器(Fragment Shader)是用于计算每个像素的颜色和光照的关键组件。这个计算过程在每个被触及的像素上并行执行,这意味着每个像素的颜色和光照计算是独立的,并且可以同时进行。这种并行执行的方式使得GPU能够在短时间内处理大量像素,提高渲染效率和图像质量。

线程级并行性(Thread-Level Parallelism)

GPU的高效性部分归功于其强大的线程级并行性。通过SIMD(单指令多数据)模型,GPU能够在多个线程中同时执行相同的指令集。这种模型允许同一操作在多个像素上同时进行,使得GPU能够迅速处理大规模的并行计算任务。具体来说,每个线程组负责处理一个像素或像素块,确保所有像素都能快速、同步地完成渲染任务,从而极大地提升了整体处理效率。

SIMD与锁步执行(Lockstep Execution)

SIMD的工作原理

在SIMD处理模式下,GPU可以同时对多个像素(例如一个2x2的像素块)执行相同的操作。这种方式被称为“锁步执行”(Lockstep Execution),即所有像素同步进行计算。这种同步性使得GPU能够以极高的效率完成大量像素的渲染任务。

高延迟操作处理

然而,在某些情况下,例如纹理查找,可能会出现高延迟操作。当一个像素需要执行这样的操作时,整个像素组可能会被“暂停”以等待操作完成。这种情况下,GPU不会让资源闲置,而是会切换到其他线程组继续执行其他可进行的任务,最大限度地利用硬件资源。这种灵活的任务调度机制确保了GPU在处理复杂场景时的高效性。

image-20240815150103847

纹理映射(Texture Mapping)

预存储图像与纹理映射

在渲染复杂场景时,有些背景或图案(如烟花或标志)并不是实时生成的,而是预先存储的图像。这些预存储的图像通过纹理映射技术被“贴”在三维几何体的表面上。这种方式使得GPU能够高效地生成复杂的图像效果,而无需在每次渲染时都重新计算这些图案。

纹理单元的作用

纹理单元是负责将屏幕上的像素与纹理中的像素进行映射的硬件组件。它通过将纹理图像应用到几何体的表面,使得复杂的图像效果得以实现。例如,通过纹理单元,可以在三维物体上显示逼真的图案、细节和质感,从而增强视觉效果。

采样问题与各向异性过滤(Anisotropic Filtering)

采样问题

当纹理的大小与显示区域不匹配时,可能会出现采样问题。例如,如果纹理图像较小而显示区域较大,那么在渲染时可能会出现像素化的问题,因为系统需要将较少的纹理数据放大到较大的显示区域上。相反,如果纹理图像过大而显示区域较小时,可能会出现细节丢失或模糊的现象。这些问题在快速移动的场景中尤其明显,影响图像的清晰度和细节。

各向异性过滤

为了解决这些采样问题,现代GPU引入了各向异性过滤技术。这种技术可以在处理不同角度和距离的纹理映射时提供更好的视觉效果,减少锯齿和模糊问题。各向异性过滤通过对多个采样点进行加权平均,使得最终的渲染结果更加清晰和真实,尤其是在处理斜角或复杂表面时,这种技术能够显著提升图像质量,属于之前提到的抗锯齿技术的一部分。

输出阶段(Output Stage)

像素写入帧缓冲区

在图形渲染的最后阶段,所有已经处理过的像素被写入到帧缓冲区(Framebuffer)。帧缓冲区是一块专门用于存储最终渲染图像数据的内存区域,通常位于显存中。它汇集了整个图形管线生成的像素数据,为最终图像的显示做好准备。

通知显示设备

当所有像素数据都成功写入帧缓冲区后,GPU会通知显示设备(如显示器)数据已准备完毕。显示设备从帧缓冲区中读取数据,将其转换为可见图像,显示在屏幕上。这个过程通常以每秒60次或更高的频率重复进行,以确保画面流畅。

其他测试步骤

在输出阶段,可能还会执行一些额外的测试步骤,例如:

  • 混合测试(Blend Test):处理图像中的透明度效果,将前景和背景像素颜色进行混合,以呈现透明或半透明效果。
  • 模板测试(Stencil Test):控制某些像素的渲染或屏蔽,用于实现复杂的图像叠加或图形遮罩效果。这些测试使得最终图像更加复杂和精细。

image-20240815150332151

帧缓冲区中的中间状态

中间渲染状态

在渲染过程中,帧缓冲区可能会处于一种“中间状态”,即部分像素已经完成处理并写入帧缓冲区,而其他部分像素仍在处理中。只有当所有像素都完成渲染并通过所有测试后,整个图像才会被完整地显示在屏幕上。这种中间状态有助于分阶段处理复杂的图像数据,提高整体渲染效率。

SAXPY算法示例

SAXPY算法简介

SAXPY(Single-Precision A·X Plus Y)是一种常见的线性代数操作,用于计算形如 ( Y = a \times X + Y ) 的表达式,其中 ( X ) 和 ( Y ) 是向量,( a ) 是一个标量。这个操作在数值计算和科学计算中非常常见。在本示例中,算法进一步简化,只执行乘法部分,即 ( Y = a \times X ),其中 ( X ) 是一个浮点数组,( Y ) 是输出数组。

单线程CPU实现

在CPU上,可以使用C语言进行单线程实现。该实现通过遍历整个数组,对每个元素执行乘法操作,并将结果存储在输出数组 ( Y ) 中。这种方式适用于小规模数据集,因其操作简单且不涉及复杂的并行计算。

image-20240815151925690

GPU并行实现

Metal语言实现

在GPU上,可以使用Metal语言(Apple的GPU编程语言)实现相同的SAXPY操作。与单线程的C语言实现不同,Metal语言允许在GPU上并行执行此操作。这种并行计算能力使得GPU在处理大型数据集时的性能大大优于单线程CPU。

image-20240815151956462

执行模型

GPU的执行模型利用线程级并行性来加速计算。在该模型下,多个线程同时处理不同的数组元素或像素。这种并行处理方式显著提高了计算速度,特别是在面对大规模数据集时,GPU的优势更加明显。

image-20240815152038925

CPU与GPU性能对比

小数据集上的表现

在处理较小的数据集时,CPU往往表现更好。原因在于GPU的初始化和设置开销较大,而这些小任务不足以充分利用GPU的并行计算优势。由于GPU的启动成本,单线程CPU在处理小数据集时,通常能够更快速地完成任务。

大数据集上的表现

随着数据集规模的增加,GPU的性能优势逐渐显现。由于GPU可以并行处理大量数据,其计算速度远远超过了单线程的CPU。当数据集变得非常庞大时,GPU的执行时间显著短于CPU,使其成为大数据处理的理想选择。

内存带宽的影响

GPU的内存带宽优势

除了在并行计算单元数量上的优势,GPU还具有更高的内存带宽。GPU能够更快地移动和处理大块数据,这一特性使得它在处理大型数据集时表现尤为出色。高内存带宽使得GPU在进行复杂计算时,数据传输不会成为瓶颈,从而显著提高整体性能。

CPU的限制

在本示例中,CPU被设定为单核或单线程模式,这使得它在处理大规模计算时容易受到内存带宽的限制。相比之下,CPU在内存带宽和数据传输速度方面不如GPU,因此在处理大量数据时,CPU的表现通常逊色于GPU。

性能可视化

image-20240815152115722

通过比较不同缓冲区大小的执行时间,可以直观地看到,随着数据集的增大,GPU逐渐在性能上超越CPU。特别是在最大的缓冲区大小下,GPU的执行速度几乎是CPU的七倍。这种显著的性能提升展示了GPU在大规模并行计算中的优势。

对数刻度图表

对数刻度图表展示了数组大小与执行时间之间的关系。显示了随着数组大小的增加,GPU相较于CPU的性能优势越来越明显。对数刻度能够更直观地展示在不同数据规模下,CPU和GPU性能差异的增长趋势。

通过比较SAXPY算法在CPU和GPU上的实现及性能差异,可以看出GPU在处理大规模数据时的优势。尽管在小数据集上CPU表现更好,但随着数据集的增大,GPU的并行计算能力和内存带宽优势使其成为大规模计算的理想选择。这种性能优势在处理大数据集时尤为明显,GPU能够在短时间内完成大量计算任务,大幅提升效率。

总结

最大化并行性(Parallelism)

要充分发挥GPU的高效性能,关键在于最大化并行性。GPU通过SIMD(单指令多数据流)模型,能够在多个数据元素上同时执行相同的指令。这种并行处理大大提高了GPU的吞吐量,使其在处理大量数据时表现得尤为出色,特别是在图形渲染和计算密集型任务中。

固定功能单元

尽管GPU的主要优势在于其强大的并行计算能力,但它也包含一些固定功能单元,如纹理处理单元(Texture Processing Units)。这些单元专门用于加速特定类型的数学运算,如纹理映射、滤波和光照计算等,从而进一步提高图形处理的速度和效率。这些固定功能单元使得GPU在处理特定图形任务时更加高效。

通用计算扩展

现代GPU的应用已经远远超出了传统的游戏和图形渲染领域。随着并行计算需求的增加,GPU被广泛应用于通用计算(GPGPU)。例如,Nvidia的CUDA和AMD的ROCm平台使得开发者能够利用GPU的并行计算能力,在科学计算、数据分析、机器学习和神经网络等领域进行高效运算。

增长因素

随着机器学习、人工智能(AI)和大数据分析的快速发展,GPU在这些领域的需求显著增加。这些应用程序通常需要处理大量并行计算任务,这正是GPU的强项。因此,GPU技术在这些领域得到了广泛应用和进一步发展,推动了硬件和软件技术的进步,使得GPU成为现代计算领域中不可或缺的部分。

GPU的架构和设计使其在图形渲染和通用计算中都表现出色。通过最大化并行性和利用固定功能单元,GPU不仅在传统的图形处理领域中发挥了重要作用,还在现代科学计算和人工智能领域取得了广泛应用。随着技术的发展,GPU在各个领域的应用将会进一步扩大,推动更多计算密集型任务的高效执行。


© 2024 LzzsSite
该笔记由 LzzsG 基于 CS 61C / Garcia, Yan 的作品创作,采用的是 CC BY-NC-SA 许可。