游戏玩家在以近乎电影般的细节渲染的虚拟世界中漫步。几秒钟后,屏幕上充满了 3D 爆炸效果,这是隐藏在物理上精确的阴影中的看不见的敌人造成的。用户失望地退出游戏,回到计算机桌面,桌面展现了现代窗口管理器时尚的 3D 外观。这两种视觉体验都需要数百 Gflops 的计算性能,这种需求由每台消费级 PC 中都存在的 GPU(图形处理单元)来满足。
现代 GPU 是一种通用的处理器,它构成了不断增长的多核并行计算架构领域中一个极端但引人注目的点。这些平台,包括 GPU、STI Cell Broadband Engine、Sun UltraSPARC T2,以及越来越多的来自 Intel 和 AMD 的多核 x86 系统,通过优先考虑许多并行操作的高吞吐量处理,而不是单个任务的低延迟执行,将自己与传统的 CPU 设计区分开来。
GPU 汇集了大量的固定功能和软件可编程处理资源。在讨论 GPU 设计时,经常会出现令人印象深刻的统计数据,例如 ALU(算术逻辑单元)计数和峰值浮点速率。然而,尽管图形本质上是并行的,但将常见的渲染算法有效地映射到 GPU 资源上仍然极具挑战性。
高性能的关键在于硬件组件及其相应的软件接口所使用的策略,以保持 GPU 处理资源的繁忙。GPU 设计竭尽全力获得高效率,从而方便地降低了程序员在编程图形应用程序时面临的难度。因此,GPU 提供了高性能,并公开了一个富有表现力但简单的编程接口。该接口在很大程度上仍然没有显式的并行性或异步执行,并且已被证明可以在供应商实现和几代 GPU 设计之间移植。
在 CPU 平台向面向吞吐量的转变引发了对并行编程复杂性的担忧之际,理解 GPU 计算成功背后的关键思想不仅对针对 GPU 执行的软件开发人员有价值,而且对为其他领域的新架构和编程系统提供信息也很有价值。在本文中,我们将深入现代 GPU 的内部,了解交互式渲染为何具有挑战性,并探索 GPU 架构师为应对这些挑战而设计的解决方案。
图形系统生成表示虚拟场景视图的图像。该场景由物体表面的几何形状、方向和材质属性以及光源的位置和特征定义。场景视图由虚拟相机的位置描述。图形系统力求在实现最大性能和保持用于描述图形计算的富有表现力但简单的接口这两个相互冲突的目标之间找到适当的平衡。
Direct3D 和 OpenGL 等实时图形 API 通过将渲染计算表示为图形处理管线来实现这种平衡,该管线对四个基本实体执行操作:顶点、图元、片段和像素。图 1 提供了简化的七阶段图形管线的框图。数据在实体流中的阶段之间流动。该管线包含实现 API 指定操作的固定功能阶段(棕褐色)和三个可编程阶段(棕色),这些阶段的行为由应用程序代码定义。图 2 说明了关键管线阶段的操作。
VG(顶点生成)。 实时图形 API 将表面表示为简单几何图元(点、线或三角形)的集合。每个图元由一组顶点定义。为了启动渲染,应用程序向管线的 VG 阶段提供顶点描述符列表。VG 从此列表中从内存中预取顶点数据,并构建顶点数据记录流以供后续处理。实际上,每个记录都包含顶点的 3D (x,y,z) 场景位置以及附加的应用程序定义的参数,例如表面颜色和法线向量方向。
VP(顶点处理)。 VP 的行为是应用程序可编程的。VP 独立地对每个顶点进行操作,并从每个输入记录生成恰好一个输出顶点记录。VP 执行的最重要操作之一是计算 3D 顶点位置的 2D 输出图像(屏幕)投影。
PG(图元生成)。 PG 使用应用程序提供的顶点拓扑数据将来自 VP 的顶点分组为有序的图元流(每个图元记录是几个 VP 输出顶点记录的连接)。顶点拓扑还定义了输出流中图元的顺序。
PP(图元处理)。 PP 独立地对每个输入图元进行操作,以生成零个或多个输出图元。因此,PP 的输出是一个新的(可能更长或更短的)有序图元流。与 VP 一样,PP 操作也是应用程序可编程的。
FG(片段生成)。 FG 在屏幕空间中密集地采样每个图元(此过程称为光栅化)。每个样本都表现为 FG 输出流中的片段记录。片段记录包含表面样本的输出图像位置、其与虚拟相机的距离,以及通过插值源图元的顶点参数计算的值。
FP(片段处理)。 FP 模拟光与场景表面的交互,以确定每个片段采样点处的表面颜色和不透明度。为了使表面具有逼真的外观,FP 计算大量使用经过滤的查找,查找对象是称为纹理的大型参数化 1D、2D 或 3D 数组。FP 是一个应用程序可编程阶段。
PO(像素操作)。 PO 使用每个片段的屏幕位置来计算并将片段对输出图像像素值的贡献应用到图像像素值。PO 考虑了样本与虚拟相机的距离,并丢弃了被更靠近相机的表面遮挡的片段。当来自多个图元的片段对单个像素的值做出贡献时(当半透明表面重叠时经常发生这种情况),许多渲染技术依赖 PO 来按照 PP 输出流中图元的位置定义的顺序执行像素更新。所有图形 API 都保证此行为,并且 PO 是唯一一个实体处理顺序由管线定义指定的阶段。
应用程序可编程管线阶段(VP、PP、FP)的行为由着色器函数(或着色器)定义。图形程序员使用高级着色语言(例如 NVIDIA 的 Cg、OpenGL 的 GLSL 或 Microsoft 的 HLSL)来表达顶点、图元和片段着色器函数。着色器源代码被离线编译为字节码,然后在运行时由图形驱动程序转换为特定于 GPU 的二进制文件。
着色语言支持复杂的数据类型和丰富的控制流结构,但它们不包含与显式并行执行相关的原语。因此,着色器定义是一个类似 C 语言的函数,它从单个输入实体串行计算输出实体数据记录。每个函数调用都被抽象为独立控制序列,该序列与对其他流实体的处理完全隔离地执行。
为了方便起见,除了来自阶段输入和输出流的数据记录外,着色器函数还可以访问(但不能修改)大型的、全局共享的数据缓冲区。在管线执行之前,这些缓冲区被初始化为包含应用程序的特定于着色器的参数和纹理。
图形管线执行的特点是以下关键属性。
并行处理的机会。 图形提供了任务并行(跨管线阶段)和数据并行(阶段独立地对流实体进行操作)的机会,使并行处理成为提高吞吐量的可行策略。然而,尽管存在丰富的潜在并行性,但 PO 阶段处理顺序的约束引入了动态的、细粒度的依赖关系,这使得在整个管线中并行实现变得复杂。虽然大多数片段的输出图像贡献可以并行应用,但那些对同一像素做出贡献的片段则不能。
固定功能阶段封装了难以并行的工作。 每个着色器函数调用都是串行执行的;然而,可编程阶段通过同时在多个流实体上执行着色器函数而可以轻松地并行化。相比之下,管线的不可编程阶段涉及多个实体交互(例如 PO 中的排序依赖关系或 PG 中的顶点分组)和有状态处理。将这种非数据并行工作隔离到固定阶段,可以保持着色器编程模型的简单性,并允许 GPU 的可编程处理组件高度专门化用于数据并行执行。此外,这种分离使得图形计算的困难方面可以封装在优化的固定功能硬件组件中。
管线负载的极端变化。 虽然图形管线的阶段数和数据流是固定的,但所有阶段的计算和带宽要求都因着色器函数的行为和场景的属性而异。例如,覆盖屏幕大区域的图元比顶点生成更多的片段。相比之下,许多小图元导致高的顶点处理需求。应用程序经常重新配置管线以使用不同的着色器函数,这些函数的指令从几十条到几百条不等。由于这些原因,在单个帧的处理期间,不同的阶段将主导整体执行,通常会导致执行的带宽和计算密集型阶段。在这种可变性面前,保持图形管线到 GPU 资源的有效映射是一个巨大的挑战,因为它需要根据当前负载将处理和片上存储资源动态地重新分配给管线阶段。
可预测和不可预测的数据访问的混合。 图形管线使用实体流严格定义了阶段间数据流。这种可预测性为聚合预取流数据记录和高度专门化的硬件管理片上存储资源提供了机会。相比之下,着色器执行的缓冲区和纹理访问是对动态计算地址的细粒度内存操作,这使得预取变得困难。由于这两种形式的数据访问对于维持高吞吐量至关重要,因此着色器编程模型显式地区分了流与缓冲区/纹理内存访问,从而允许针对这两种类型的访问采用专门的硬件解决方案。
指令流共享的机会。 虽然着色器编程模型允许每个着色器调用遵循唯一的控制流,但在实践中,附近流元素上的着色器执行通常会导致相同的动态控制流决策。因此,多个着色器调用可能共享一个指令流。尽管 GPU 必须适应并非如此的情况,但在多个着色器调用之间共享指令流是 GPU 处理内核设计中的一个关键优化,并在管线调度算法中得到考虑。
GPU 的大部分资源都存在于负责执行着色器函数的可编程处理内核中。虽然不同供应商和产品线之间存在很大的实现差异,但所有现代 GPU 都通过使用多核设计来保持高效率,这些设计同时采用硬件多线程和 SIMD(单指令多数据)处理。如表 1 所示,这些吞吐量计算技术并非 GPU 独有(前两行)。然而,与 CPU 相比,GPU 设计将这些思想推向了极端规模。
多核 + SIMD 处理 = 大量 ALU。 控制线程由处理器指令流实现,这些指令在处理器管理的环境(称为执行(或线程)上下文)中执行。此上下文由状态组成,例如程序计数器、堆栈指针、通用寄存器和虚拟内存映射。多核处理器复制处理资源(ALU 和执行上下文),并将它们组织成独立的内核。当应用程序具有多个控制线程时,多核架构通过在每个内核上并行执行这些指令流来提高吞吐量。例如,Intel Core 2 Quad 包含四个内核,可以同时执行四个指令流。由于着色器调用之间存在显着的并行性,GPU 设计很容易将内核数量推得更高。高端型号每个芯片包含多达 16 个内核。
通过在每个内核中填充多个浮点 ALU,可以实现更高的性能。这可以通过 SIMD 处理有效地完成,SIMD 处理使用每个 ALU 对不同的数据块执行相同的操作。SIMD 处理最常见的实现是通过显式短向量指令,类似于 x86 SSE 或 PowerPC Altivec ISA 扩展提供的指令。这些扩展提供了宽度为 4 的 SIMD,指令控制四个 ALU 的操作。替代实现(例如 NVIDIA 的 8 系列架构)通过隐式在具有相同 PC 的多个线程之间共享指令来执行 SIMD 执行。在任何一种 SIMD 实现中,处理指令流的复杂性以及控制 ALU 的电路和结构的成本都在多个 ALU 之间分摊。结果是功耗和面积效率都很高的芯片执行。
CPU 设计已经收敛到宽度为 4 的 SIMD,作为提供更高吞吐量和保持高单线程性能之间的平衡。着色工作负载的特性使得 GPU 采用明显更宽的 SIMD 处理(宽度范围从 32 到 64)并支持丰富的操作集变得有利。GPU 通常支持倒数平方根、三角函数和内存收集/分散操作的 SIMD 实现。
宽 SIMD 处理的效率允许 GPU 密集地填充许多带有 ALU 的内核。例如,NVIDIA GeForce 8800 Ultra GPU 包含 128 个以 1.5 GHz 运行的单精度 ALU。这些 ALU 被组织成 16 个处理内核,并产生 384 Gflops 的峰值速率(每个 ALU 每个时钟周期完成一个 32 位乘加运算)。相比之下,高端 3 GHz Intel Core 2 CPU 包含四个内核,每个内核带有八个 SIMD 浮点 ALU(每个时钟周期两条 4 宽向量指令),最多可实现 96 Gflops 的峰值性能。
GPU 并行执行多组着色器调用,以利用 SIMD 处理。动态的按实体控制流通过执行着色器调用所采用的所有控制路径来实现。不适用于所有调用的 SIMD 操作(例如着色器代码条件或循环块中的操作)使用写入掩码部分地无效。在这种实现中,当着色器控制流发散时,只有更少的 SIMD ALU 做有用的工作。因此,在宽度为 S 的 SIMD 处理芯片上,最坏情况下的行为会产生等于芯片峰值速率 1/S 的性能。幸运的是,着色器工作负载表现出足够的指令流共享水平,足以证明宽 SIMD 实现的合理性。此外,GPU ISA 包含特殊的指令,使着色器编译器可以将按实体控制流转换为高效的 SIMD 操作序列。
硬件多线程 = 高 ALU 利用率。 线程停顿对高性能着色器执行提出了额外的挑战。当处理器由于依赖于未完成的指令而无法分派指令流中的下一条指令时,线程会停顿(或阻塞)。高延迟的片外内存访问,最值得注意的是由片段着色器纹理操作生成的内存访问,会导致线程停顿持续数百个周期(回想一下,虽然着色器输入和输出记录适合流式预取,但纹理访问则不然)。
在线程停顿期间允许 ALU 保持空闲是低效的。相反,GPU 在芯片上维护比它们可以同时执行的更多的执行上下文,并且当其他线程停顿时,它们执行来自可运行线程的指令。硬件调度逻辑确定在每个处理器周期中执行哪个(哪些)上下文。这种过度配置具有线程上下文的内核以隐藏线程停顿延迟的技术称为硬件多线程。GPU 使用多线程来隐藏内存访问和指令管线延迟。
GPU 多线程的延迟隐藏能力取决于硬件线程上下文与每个时钟周期可以同时执行的线程数之比(表 1 中的值 T)。对更多线程上下文的支持允许 GPU 隐藏更长或更频繁的停顿。所有现代 GPU 都在芯片上维护大量的执行上下文,以提供最大的内存延迟隐藏能力(T 的范围为 16 到 96)。这代表了与 CPU 设计的显着背离,CPU 设计试图通过大型、低延迟数据缓存和复杂的乱序执行逻辑来避免或最小化停顿。当前的 Intel Core 2 和 AMD Phenom 处理器每个内核维护一个线程,即使是 Sun 的多线程 UltraSPARC T2 处理器的高端型号也仅管理它们可以同时执行的线程数的四倍。
请注意,在没有停顿的情况下,单线程和多线程处理器的吞吐量是等效的。多线程不会增加芯片上的处理资源数量。相反,它是一种交错执行多个线程以更有效地使用现有资源(提高吞吐量)的策略。平均而言,以峰值速率运行的多线程内核在 1/T 的时间内运行每个线程。
大规模多线程需要执行上下文紧凑,以便在片上内存中容纳许多上下文。GPU 内核支持的线程上下文数量取决于着色器程序,并且通常受到片上存储大小的限制。GPU 要求编译后的着色器二进制文件声明输入和输出实体大小,以及执行所需的临时存储和暂存寄存器的边界。在运行时,GPU 使用这些边界在执行上下文之间动态地分区不可溢出的片上存储(包括数据寄存器)。因此,当着色器使用较少资源时,GPU 支持许多线程上下文(最多达到架构特定的边界),并相应地提供最大的延迟隐藏能力。当着色器需要大量存储时,GPU 提供的执行上下文数量会减少。(随附的侧边栏详细介绍了在 GPU 内核上有效执行片段着色器的示例。)
GPU 的可编程内核与一组专门的固定功能处理单元互操作,这些单元为非着色器阶段提供高性能、高能效的实现。这些组件不仅仅是增强可编程处理;它们执行复杂的操作,并构成额外的数百 Gflops 的处理能力。通过固定功能硬件执行的最重要的两个操作是纹理过滤和光栅化(片段生成)。
纹理处理几乎完全由固定功能逻辑处理。纹理操作采样连续的 1D、2D 或 3D 信号(纹理),该信号由颜色值的多维数组离散表示(2D 纹理数据只是图像)。GPU 纹理过滤单元接受纹理参数化中的一个点(由浮点元组表示,例如 {.5,.75}),并从内存中加载坐标周围的数组值。然后对这些值进行过滤,以产生单个结果,该结果表示指定坐标处的纹理值。此值返回给调用着色器函数。生成高质量图像需要复杂的纹理过滤。由于图形 API 提供了有限的过滤内核集,并且由于过滤内核在计算上是昂贵的,因此纹理过滤非常适合固定功能处理。
FG 阶段中的图元光栅化是由固定功能组件实现的另一个关键管线操作。光栅化涉及密集采样图元(至少每个输出图像像素一次),以确定图元与哪些像素重叠。此过程涉及插值每个采样点处表面的位置,然后为图元覆盖的所有采样点生成片段。边界框计算和分层技术优化了光栅化过程。尽管如此,光栅化仍然涉及大量的计算。
除了用于纹理处理和光栅化的组件外,GPU 还包含用于诸如表面可见性确定、输出像素合成和数据压缩/解压缩等操作的专用硬件组件。
并行处理资源给 GPU 的内存系统带来了极端的负载,该系统为来自固定功能和可编程组件的内存请求提供服务。这些请求包括细粒度和批量预取操作的混合,甚至可能需要实时保证(例如显示扫描输出)。
回想一下,GPU 的可编程内核通过硬件多线程来容忍大的内存延迟,并且阶段间流数据访问可以预取。因此,GPU 内存系统的架构设计旨在提供高带宽而不是低延迟的数据访问。高吞吐量是通过使用宽内存总线和专门的 GDDR(图形双倍数据速率)内存获得的,当内存访问粒度较大时,GDDR 内存的运行效率最高。因此,GPU 内存控制器必须缓冲、重新排序,然后合并大量的内存请求,以合成大型操作,从而有效地利用内存系统。例如,ATI HD 2700XT 内存控制器操纵数千个未完成的请求,以从连接到 512 位总线的 GDDR3 内存提供每秒 105 GB 的带宽。
GPU 数据缓存满足与 CPU 缓存不同的需求。GPU 采用相对较小的只读缓存(无缓存一致性),这些缓存过滤注定要用于内存控制器的请求,并降低对主内存的带宽要求。因此,GPU 缓存通常用于放大处理单元的总带宽,而不是减少内存访问的延迟。许多线程的交错执行使得大型读写缓存效率低下,因为会发生严重的缓存抖动。GPU 受益于小型缓存,这些缓存捕获同时执行的着色器调用之间的空间局部性。这种情况很常见,因为在处理屏幕上紧密相邻的片段时执行的纹理访问很可能具有重叠的纹理过滤器支持区域。
虽然大多数 GPU 缓存都很小,但这并不意味着 GPU 包含很少的片上存储。大量的片上存储用于保存实体流、执行上下文和线程暂存数据。
将整个图形管线有效地映射到 GPU 资源上是一个具有挑战性的问题,需要动态和自适应技术。GPU 计算的一个独特方面是硬件逻辑在将计算映射和调度到芯片资源上起着重要作用。GPU 硬件“调度”逻辑超出了前面章节中讨论的线程调度职责。GPU 自动将计算分配给线程,在线程完成后进行清理,调整和管理保存流数据的缓冲区大小,在需要时保证有序处理,并识别和丢弃不必要的管线工作。这种逻辑在很大程度上依赖于对图形工作负载特性的特定预先了解。
传统的线程编程使用操作系统或线程 API 机制来创建、完成线程并在共享结构上同步。然而,大规模多线程与着色器函数执行的简短性(最多几百条指令)相结合,意味着 GPU 线程管理必须完全由硬件逻辑执行。
GPU 通过预先配置执行上下文以运行管线的三种类型的着色器函数之一,并为相同类型的着色器多次重用配置,从而最大限度地降低线程启动成本。当着色器阶段的输入流包含足够数量的实体时,GPU 启动线程,然后它们自动为线程提供对着色器输入记录的访问权限。类似的硬件逻辑在线程完成时将记录提交到输出流缓冲区。执行上下文到着色器阶段的分配会定期重新配置,因为管线需求发生变化,并且流缓冲区耗尽或接近容量。
GPU 利用对管线实体的预先了解来识别和跳过不必要的计算。例如,由多个图元共享的顶点被识别,并且 VP 结果被缓存以避免重复的顶点处理。当片段不会改变任何图像像素的值时,GPU 也会在 FP 之前丢弃片段。当片段的采样点被先前处理的位于更靠近相机的表面遮挡时,会触发早期片段丢弃。
另一类硬件优化重新组织细粒度操作以实现更有效的处理。例如,光栅化对片段生成进行排序,以最大化样本的屏幕邻近度。这种排序提高了纹理缓存命中率,以及跨着色器调用的指令流共享。当 GPU 内存控制器重新排序内存请求以优化内存总线和 DRAM 利用率时,它也会执行自动重新组织。
GPU 使用硬件逻辑确保片段间 PO 排序依赖性。实现使用诸如后 FP 重新排序缓冲区或记分板之类的结构,这些结构会延迟片段线程启动,直到重叠片段的处理完成。
GPU 硬件可以负责复杂的调度决策,因为图形管线的语义和不变量是先验已知的。硬件实现实现了细粒度的逻辑,该逻辑由对图形管线和底层 GPU 实现的精确知识提供信息。因此,GPU 在使用所有可用资源方面非常有效。这种方法的缺点是 GPU 仅执行那些已知其不变量和结构的计算。
图形编程正变得越来越通用。开发人员不断寻求整合更复杂的算法并利用更多可配置的图形管线。与此同时,GPGPU(使用 GPU 平台进行通用计算)的日益普及导致了访问 GPU 资源的新接口。鉴于这两种趋势,GPU 设计人员可以将计算的先验知识嵌入到硬件调度逻辑中的程度将不可避免地随着时间的推移而降低。
GPU 编程发展中的一个主要挑战是在提高应用程序接口的通用性和表达能力的同时,保持 GPU 性能水平。GPGPU 接口的设计,例如 NVIDIA 的 CUDA 和 AMD 的 CAL,证明了这一挑战的难度。这些框架将计算抽象为大型批处理操作,这些操作涉及并行运行的内核函数的多次调用。由此产生的计算仅在大量数据并行性的条件下才能在 GPU 上高效执行。尝试实现非数据并行算法的程序性能不佳。
GPGPU 编程模型易于使用,并且允许编写良好的程序充分利用 GPU 可编程内核和(如果需要)纹理资源。然而,使用这些接口的程序无法使用芯片强大的固定功能组件,例如与压缩、图像合成或光栅化相关的组件。此外,当启用这些接口时,许多特定于图形管线调度的逻辑 просто 被关闭了。因此,当前的 GPGPU 编程框架限制了计算,使得它们的结构以及它们对芯片资源的使用保持足够简单,以便 GPU 可以并行运行这些程序。
现代图形处理器是一个强大的计算平台,它位于面向吞吐量架构的设计空间的极端端。GPU 的处理资源和随附的内存系统经过高度优化,可以并行执行大量操作。此外,针对图形领域的专业化使得固定功能处理的使用成为可能,并允许硬件调度并行计算变得实用。通过这种设计,GPU 为具有挑战性的工作负载提供了无与伦比的性能水平,同时为开发人员保持了简单方便的编程接口。
今天,商品 CPU 设计正在采用 GPU 计算中常见的特性,例如增加内核数量和硬件多线程。与此同时,每一代 GPU 的发展都为以前的高吞吐量 GPU 设计增加了灵活性。鉴于这些趋势,许多领域的软件开发人员可能会对 CPU 和 GPU 架构以及相应的 CPU 和 GPU 编程系统最终融合的程度感兴趣。
KAYVON FATAHALIAN 是斯坦福大学计算机图形实验室计算机科学博士候选人。他的研究兴趣包括商品并行架构的编程系统以及用于交互式和电影领域的计算机图形/动画系统。他的论文研究旨在使未来的 GPU 和多核 PC 能够执行更灵活的渲染管线。他很快将开始找工作。
MIKE HOUSTON 是斯坦福大学计算机图形实验室计算机科学博士候选人。他的研究兴趣包括用于并行架构(包括 GPU、Cell、多核 CPU 和集群)的编程模型、算法和运行时系统。他的论文包括 Sequoia 运行时系统,这是一个用于编程分层内存机器的系统。他于 2001 年在 UCSD 获得计算机科学学士学位,并且是英特尔研究生奖学金的获得者。
着色器编译为 SIMD(单指令多数据)指令序列与动态硬件线程调度相结合,导致在图 A 所示的简化单核 GPU 上高效执行片段着色器。
图形驱动程序进行的着色器编译从高级片段着色器源生成 GPU 二进制文件。生成的向量指令序列通过在宽度为 32 的向量的单个通道中执行每个调用来同时执行 32 个片段着色器调用。编译后的二进制文件需要四个向量寄存器用于临时结果,并且在每个纹理访问操作之间包含 20 条算术指令。
在运行时,GPU 在其四个线程上下文中的每一个上执行着色器二进制文件的副本,如图 B 所示。内核执行 T0(线程 0),直到在周期 20 中检测到由纹理访问引起的停顿。当 T0 等待纹理操作的结果时,内核继续执行其剩余的三个线程。T0 的纹理访问结果在周期 70 中变为可用。在周期 80 中 T3 停顿后,内核立即恢复 T0。因此,在执行期间的任何时候,ALU 都不会处于空闲状态。
当为此示例执行着色器程序时,至少需要四个线程才能使内核 ALU 保持繁忙。每个线程同时对 32 个片段进行操作;因此,芯片要实现峰值性能,需要 4*32=128 个片段。
由于真实 GPU 上的内存延迟涉及数百个周期,因此现代 GPU 必须包含对更多线程的支持,以维持高利用率。如果我们把简单的 GPU 扩展到更实际的规模,即八个处理核心,并为每个核心配置 16 个执行上下文的存储,那么就需要同时处理 4,096 个片段才能接近峰值处理速率。显然,GPU 性能在很大程度上依赖于并行着色工作量的充足性。
最初发表于 Queue 杂志第 6 卷第 2 期—
在 数字图书馆 中评论这篇文章
David Crandall, Noah Snavely - 利用互联网照片集对人物和地点进行建模
本文介绍了我们利用在线照片集重建关于世界及其居民在全球和本地范围内信息的工作。这项工作得益于社交内容共享网站的 dramatic 增长,这些网站创建了大量的用户生成视觉数据的在线集合。仅 Flickr.com 目前就托管着超过 60 亿张由超过 4000 万独立用户拍摄的图片,而 Facebook.com 表示每天增长近 2.5 亿张照片。
Jeffrey Heer, Ben Shneiderman - 用于视觉分析的交互式动态
数字数据的规模和可用性不断提高,为公共政策、科学发现、商业策略,甚至我们的个人生活提供了非凡的资源。然而,为了充分利用这些数据,用户必须能够理解它:提出问题,发现感兴趣的模式,并识别(并可能纠正)错误。与数据管理系统和统计算法相结合,分析需要针对数据中发现的集群、趋势和异常值的领域特定意义进行情境化的人工判断。
Robert DeLine, Gina Venolia, Kael Rowan - 使用代码地图进行软件开发
为了更好地理解专业软件开发人员如何使用代码的可视化表示,我们采访了微软的九位开发人员,以确定常见的场景,然后调查了 400 多位开发人员,以更深入地理解这些场景。
Brendan Gregg - 可视化系统延迟
当 I/O 延迟以可视化热图的形式呈现时,可能会出现一些有趣而美丽的模式。这些模式提供了对系统实际运行情况以及最终用户应用程序体验到的延迟类型的深入了解。在这些模式中看到的许多特征仍然不被理解,但到目前为止,它们的分析揭示了以前未知的系统行为。