弄清楚如何在个人电脑上降低渲染应用程序的GPU帧时间可能是一项具有挑战性的任务,即使是最有经验的PC游戏开发人员也是如此。本博客文章描述了我们在NVIDIA内部使用的性能分类方法,使用NVIDIA特定的硬件度量标准来找出任何给定GPU工作负载(也称为性能标记或调用范围)的主要性能限制器。
我们的性能分类方法并非从假设或有关在GPU上呈现的内容的知识开始。相反,它仅从硬件指标开始,让我们知道整个GPU的利用情况,哪些硬件单元和子单元限制了性能,以及它们运行到各自的峰值性能的接近程度(也称为“速度光“或”SOL“)。如果应用程序不使用异步计算,则可以将此以硬件为中心的信息映射回图形API和着色器正在执行的操作,从而为如何提高任何给定工作负载的GPU性能提供指导:
如果没有GPU单元具有较高的吞吐量(与SOL相比),那么我们会努力提高至少一个单元的吞吐量。
如果一些GPU单元具有较高的吞吐量(与SOL相比),那么我们将弄清楚如何从该单元中删除工作。
Nsight Range Profiler
从开普勒架构1(完全支持Maxwell,Pascal和Volta GPU)开始,所有NVIDIA GPU上的DX11,DX12和OpenGL上的PerfWorks库可以捕获每个GPU工作负载的硬件指标。虽然PerfWorks头文件尚未公开,但该库现在可以通过公共工具使用:Nsight的Range Profiler :用于DX12,DX11和OpenGL 4.6(但不包括Vulkan)的Visual Studio Edition 5.5,以及Microsoft的“ PIX在Windows上“用于DX12。
1从维基百科上可以看到,GeForce 600和700系列大多是开普勒,900系列是麦克斯韦,1000系列是帕斯卡,而TITAN V是沃尔塔。
首先,为了获得最具确定性的测量结果,我们建议您在收集任何性能指标之前始终锁定您的GPU核心时钟频率(并在解锁之后进行解锁,以获得最佳性能,以及在GPU空闲或只是最小化功耗和噪声时做桌面渲染)。在启用了“开发者模式”的Windows 10上,可以通过运行一个简单的DX12应用程序来完成此操作,该应用程序在虚拟DX12设备上调用SetStablePowerState(TRUE),然后在不释放设备的情况下进入睡眠状态,如本博文所述。
注意:从Nsight:Visual Studio版本5.5开始,Range Profiler现在可以在分析任何Range之前/之后有效地调用SetStablePowerState(),使用适用于所有Windows版本(不仅仅是Windows 10)的内部驱动程序API,也不需要操作系统将处于“开发者模式”。因此,在使用Nsight Range Profiler时,您不必担心锁定GPU Core时钟。
对于非UWP(通用Windows平台)应用程序,这可以通过将您的EXE(或批处理文件)拖放到Nsight安装在桌面上的“ NVIDIA Nsight HUD启动器 ”快捷方式来完成,并达到您想要的游戏位置捕捉,然后:
按CTRL-Z在屏幕的右上部分显示Nsight HUD,并
点击Nsight HUD中的“ 暂停和捕捉帧 ”按钮,或者按空格键开始捕捉。
您可以通过单击“ 将捕获保存到磁盘 ”按钮将当前帧导出到Visual Studio C ++项目。(默认情况下,Nsight导出的帧被保存到C: Users ... Documents NVIDIA Nsight Captures ...)
你可以点击“ 恢复 ”继续玩你的游戏,以便找到你想要捕捉更多帧的其他位置。
注意:您可以跳过“ 将捕获保存到磁盘 ”步骤并直接跳到下一步(Scrubber&Range Profiler分析),但我们建议始终将捕获保存到磁盘,并将它们存档,以便可以返回到它们你需要稍后。将导出的帧保存到磁盘可让您稍后将数据附加到分析中,以便您或团队中的其他人员可以尝试重现您的结果。
在NVIDIA®(英伟达™)中,我们将性能分析视为一个科学流程,我们提供与分析相关的所有repro数据,并鼓励同事重新绘制并审核我们的结果。根据我们的经验,在进行性能优化尝试(无论是否成功)之前和之后捕获帧也是一种很好的做法,分析硬件度量标准如何变化并从结果中学习。
关于Nsight帧捕获的补充说明:
对于Nsight框架,无论您的应用程序是以窗口模式,全屏模式还是全屏无边界模式运行都无所谓,因为Nsight总是在隐藏窗口中运行框架。只要确保分辨率和图形设置是你想要分析的。
对于DX12应用程序,我们假设没有使用异步计算,否则某些硬件度量可能会受到GPU上多个同时执行的工作负载的影响。到目前为止,对于所有基于PerfWorks的分析,我们建议禁用在DX12应用程序中使用异步计算。
关于DX12异步复制调用(在COPY队列中),可以在帧捕获中使用这些调用,但是您应该知道,PerfWorks和Nsight目前不会单独分析COPY队列调用。因此,与其他DIRECT队列调用并行执行的任何COPY队列调用都可能会影响这些工作负载中的GPU DRAM流量。
将该exe文件拖放到“Nsight HUD启动器”将不适用于UWP应用程序。在对UWP Nsight推出的做法,目前只能通过Visual Studio IDE中的支持。
GPU时间的自顶向下查看是找出哪些性能指标/工作负载在一个帧中最昂贵的好方法。对于HBAO + DX11测试应用程序,在GeForce GTX 1060 6GB上渲染SSAO为4K,在Nsight的Scrubber中,GPU帧时间分解如下所示:
图1. Nsight中Scrubber的示例GPU帧时间细分:Visual Studio Edition 5.5。
在“ Perf Markers ”行中,Scrubber显示通过D3D时间戳查询测量的每个工作负载的GPU时间,以及每个工作负载正在使用的GPU帧时间百分比(排除当前调用)。在图1的例子中,这一帧中哪个工作负载最为昂贵是显而易见的:“DrawCoarseAOPS”,占GPU时间的49.1%。
注意:要重新显示此结果,可以从GitHub下载HBAO +源代码版本3.1,然后在Visual Studio中运行“SampleApp_D3D11”项目。要使RenderAO调用发出性能标记,可以在GFSDK_SSAO_D3D11 - >项目属性 - > C / C + - >预处理器中定义ENABLE_PERF_MARKERS = 1。为了记录,这里是框架的样子:
您可以打开您在步骤2中导出到磁盘的帧捕获的Visual Studio解决方案文件,在Release x64中构建解决方案,然后转到Visual Studio中的Nsight菜单,然后单击“ 开始图形调试 ”。
要到达Nsight Range Profiler,您可以启动Nsight帧捕捉EXE,然后:
按CTRL-Z然后按空格键
ALT-Tab到您的Visual Studio窗口
找到Nsight在那里添加的Scrubber选项卡
右键单击您想要分析的工作负载,然后单击“ Profile [Perf Markers] ... ”
注意:如果由于某种原因,CTRL-Z + SPACE不适用于您的应用程序,您可以ALT-Tab到Visual Studio,然后单击Visual Studio - > Nsight菜单 - >“暂停和捕获帧”。
我们通过右键单击洗涤器中的“DrawCoarseAOPS”框并执行“Profile [Perf Markers] DrawCoarseAOPS”,从步骤3的“DrawCoarseAOPS”工作负载调用Range Profiler(仅通过分析此调用范围,除此之外别无其他)
图2.从Scrubber窗口为给定的工作负载启动Nsight Range Profiler。
Range Profiler 在Nsight帧捕获中注入PerfWorks调用,并为指定的工作负载收集一组PerfWorks度量。分析完成后,Nsight将在Scrubber下方的Range Profiler窗口的新部分中显示收集的度量标准。
我们首先检查Range Profiler的“Pipeline Overview”Summary部分的指标。这只是PerfWorks衡量当前工作负载的一个视图。通过将鼠标悬停在任何指标上,实际的Perfworks指标名称将显示在工具提示中:
图3.显示PerfWorks度量名称和描述的Nsight Range Profiler工具提示。
要查看每个工作负载的首要顶级指标是GPU的每单元SOL%指标。这些传达了每个单元与其最大理论吞吐量或光速(SOL)的接近程度。在高层次上,可以将每单元SOL%度量设想为实现的吞吐量与SOL吞吐量的比率。但是,对于具有多个子单元或并发数据路径的单元,每单元SOL%是所有子单元和数据路径的所有子SOL标准的最大值。
注:如果您不熟悉GPU中设备的名称,请阅读以下博客文章,其中提供了逻辑图形管道如何映射到GPU架构中的GPU单元的高级概述:“ 三角形的寿命- NVIDIA的逻辑管道 “,以及GTC 2016演示文稿中的第7至第25张幻灯片:” GPU驱动渲染 “。在这种情况下:
“IA”(Input Assembler)加载索引和顶点(在顶点着色器被调用之前)。
SM(流式多处理器)运行着色器。
TEX执行SRV提取(和Maxwell以来的无人机访问)。
L2是连接到每个DRAM分区的二级缓存。
CROP的颜色写入和混合渲染目标。
ZROP进行深度模板测试。
DRAM(范围图中的“内存”)是GPU视频内存。
注意:在Nsight Range Profiler结果中,通过在管道概览中选择“Range Diagram”,可以找到图形管道的简化视图及其与GPU单元的映射。在该图中,每单位SOL%值显示为绿色条形:
图4. Nsight Range Profiler:流水线概览 - >范围图。
如图4所示,当今的GPU不是一个简单的线性流水线(A→B→C→...),而是一个互连单元网络(SM < - > TEX < - > L2,SM- > CROP < - > L2等)。简单的“瓶颈”计算依赖于每个单元的固定上行和下行接口,但不足以推断GPU的性能。因此,在进行我们的分析时,我们主要考察每个单位的SOL%度量,以确定单位和/或限制性能的问题。下一节将详细讨论这种方法。
在我们的性能分类方法论中,我们始终始于查看前5个SOL单位及其相关的SOL%度量。这些是限制此工作负载的GPU性能的前5个硬件单元。Nsight Range Profiler在Pipeline Overview - Summary部分显示前5个SOL%指标(又名“顶级SOL”),例如:
图5. Range Profiler中的“Top SOLs”示例 - > Nsight中的Pipeline Overview:Visual Studio Edition 5.5。
如果顶层SOL%值大于80%,那么我们知道GPU上的配置文件工作负载运行非常高效(接近最大吞吐量),为了加快速度,应该尝试从顶层SOL单元中移除工作,它到另一个单位。例如,对于SM作为SOL%> 80%的顶级SOL单元的工作负载,可以尝试机会性地跳过指令组,或考虑将某些计算移动到查找表。另一个例子是将结构化缓冲区加载移动到在统一访问结构化缓冲区(所有线程加载来自相同地址的数据)中的着色器中的恒定缓冲区加载,以及受纹理吞吐量限制的工作负载(因为结构化缓冲区通过TEX单元加载)。
如果最高SOL%值<60%,这意味着顶级SOL单元和所有其他具有较低SOL%的GPU单元未充分利用(空闲周期),运行效率低下(失速周期),或者未达到其快速路径,原因是他们给出的工作量的细节。这些情况的例子包括:
该应用程序部分受CPU限制(参见第6.1.1节);
大量等待空闲命令或图形< - >计算开关反复排空GPU管线(参见第6.1.2节);
TEX从具有格式,维度或过滤器模式的纹理对象中提取数据,使其在设计时以较低的吞吐量运行(请参阅这些用于GTX 1080的综合基准测试)。例如,当采用三线性滤波对三维纹理进行采样时,预计有50%的TEX SOL%;
内存子系统效率低下,例如TEX或L2单元中高速缓存命中率低,导致DRAM SOL%低的DRAM访问稀疏,VB / IB / CB / TEX从系统内存取代GPU DRAM;
输入组件获取32位索引缓冲区(半速率与16位索引相比)。
注意:在这种情况下,我们可以使用最高SOL%值来导出可通过降低低效率在此工作负载上实现的最大增益的上限:如果给定的工作负载在其SOL的50%处运行,并且假设可以通过减少内部低效率将SOL%提高到90%,我们知道工作负载的最大预期收益为90/50 = 1.8x = 80%。
在这种情况下(灰色区域),我们遵循情况1(高位SOL%)和情况2(低位SOL%)的方法。
注:每单元SOL%度量标准都是相对于GPU周期(挂钟)而定义的,这可能与活动周期(硬件单元不空闲的周期)不同。我们定义它们相对于经过周期而不是每单位有效周期的主要原因是通过给出它们所有的公分母来使SOL%指标具有可比性。定义它们相对于经过周期的另一个好处是任何限制整体GPU性能的GPU空闲周期都会被报告为该工作负载的最低SOL%值(我们的SOL引导分类中的顶级度量标准)。
Nsight Range Profiler报告前5个SOL单元的原因不仅仅是最大的一个,因为可能有多个硬件单元彼此交互,并且都在一定程度上限制了性能。所以我们建议根据SOL%值手动对SOL单元进行聚类。(实际上,10%的增量似乎可以很好地定义这些集群,但我们建议手动执行集群以避免遗漏任何内容。)
注意:我们还建议查看距离分析器“内存”部分中显示的TEX(L1)和L2命中率。一般来说,大于90%的命中率很好,80%到90%之间好,80%以下(可能会显着限制性能)。
全屏HBAO +模糊工作负载与前5个SOL:
SM:94.5%| TEX:94.5%| L2:37.3%| 作物:35.9%| DRAM:27.7%
...既是SM又是TEX限制。由于SM和TEX SOL%值是相同的,我们可以推断出SM性能很可能受SM和TEX单元之间接口吞吐量的限制:SM请求TEX或TEX将数据返回给SM 。
它具有TEX命中率88.9%和L2命中率87.3%。
在“TEX-Interface Limited Workload”附录中查看此工作负载的研究。
从HBAO +例子中转移出来,下面是我们最近分析的一些典型的游戏引擎工作负载。
具有顶级SOL的SSR工作负载:
SM:49.1%| L2:36.8%| TEX:35.8%| DRAM:33.5%| CROP:0.0%
...将SM作为主要限制器,将L2,TEX和DRAM作为次要限制器,TEX命中率为54.6%,L2命中率为76.4%。这种较差的TEX命中率可以解释SM SOL%较低:由于TEX命中率较差(很可能是由于相邻像素获取了很远的纹理元素),因此SM看到的平均TEX延迟比通常更高,更具挑战性隐藏。
注意:这里的活动单元实际上是一个依赖链:SM - > TEX - > L2 - > DRAM。
此顶级SOL的GBuffer填充工作负载:
TEX:54.7%| SM:46.0%| DRAM:37.2%| L2:30.7%| CROP:22.0%
...将TEX和SM作为主要限制器,将DRAM和L2作为次要限制器,TEX命中率为92.5%,L2命中率为72.7%。
这款平铺照明计算着色器具有顶级SOL:
SM:70.4%| L2:67.5%| TEX:49.3%| DRAM:42.6%| CROP:0.0%
...以SM&L2为主要限制器,TEX&DRAM为次要限制器,TEX命中率为64.3%,L2命中率为85.2%。
这个带有顶级SOL的阴影贴图生成工作负载:
IA:31.6%| DRAM:19.8%| L2:16.3%| VAF:12.4%| CROP:0.0%
...是IA限制的(输入汇编器)并且具有较低的SOL%。在这种情况下,将索引缓冲区格式从32位更改为16位有帮助。TEX命中率无关紧要,因为TEX不在前5名SOL单位中。L2命中率为62.6%。
完成第5步后,我们知道每个感兴趣工作负载的顶级SOL单元(GPU单元名称和最大吞吐量的百分比),以及TEX和L2命中率。
我们知道,顶级的SOL GPU单元正在限制正在研究的工作负载的性能,因为这些单元正在运行最接近其最大吞吐量的单元。我们现在需要了解什么是限制这些top-SOL单元的性能。
如上面第5.2.2节所述,有多种可能的原因。我们经常将这些称为病理 - 并且与现实生活中的患者一样,工作负荷可能同时遭受多种病症的影响。我们首先检查以下指标的值:“ GPU空闲% ”和“ SM单元活动百分比 ”。
Range Profiler 的“ GPU Idle% ”度量标准映射到“gr__idle_pct”PerfWorks度量标准。它是当前工作负载下整个图形和计算硬件管道闲置的GPU经过周期的百分比。这些“GPU空闲”周期是CPU无法为GPU提供足够快的命令的周期,因此GPU管线完全是空的,无需处理任何工作。请注意,Wait For Idle命令导致的管道排空不计为“GPU空闲”。
如果针对任何给定的GPU工作负载看到此度量标准大于1%,那么您知道此工作负载由于某种原因受CPU限制,并且此CPU工作负载的性能影响至少为1%。在这种情况下,我们建议测量以下CPU调用花费的每个工作负载的总CPU时间,然后尝试最小化最昂贵的CPU时间:
对于DX11:
冲洗{,1}
地图
UpdateSubresource {,1}
对于DX12:
等待
ExecuteCommandLists
对于DX11和DX12:
任何创建或释放呼叫
DX11说明:
ID3D11DeviceContext :: Flush强制执行命令缓冲区启动,这可能需要Flush()调用在CPU上停止。
在连续帧中映射相同的分段资源时,在STAGING资源上调用ID3D11DeviceContext :: Map可能会导致CPU因资源争用而停顿。在这种情况下,当前帧中的Map调用必须在内部等待,直到前一帧(使用相同的资源)在返回之前已经被处理。
使用DX11_MAP_WRITE_DISCARD调用ID3D11DeviceContext :: Map可能会导致CPU由于驱动程序用尽版本空间而停顿。这是因为每次执行Map(WRITE_DISCARD)调用时,驱动程序都会返回一个指向固定大小内存池的新指针。如果驱动程序用尽版本空间,Map调用将停止。
DX12注意事项:
每个ExecuteCommandLists(ECL)调用都有一些与之关联的GPU空闲开销,用于启动新的命令缓冲区。因此,为了减少GPU空闲时间,我们建议将所有命令列表分配到尽可能少的ECL调用,除非您真的想要在帧中的某些点发生命令缓冲区启动(例如,为了减少VR应用程序中的输入延迟在飞行中有一个框架)。
当应用程序调用ID3D12CommandQueue :: Wait时,操作系统(Windows 10)将暂停向该命令队列的GPU提交新的命令缓冲区,直到等待调用返回。
注意:每个API调用的CPU时间可以使用Nsight通过捕获一个帧,然后进入Visual Studio - > Nsight - > Windows - > Events来测量:
“ SM单元活跃% ”度量标准报告每个SM实例至少有1个warp(32个线程)活动周期的百分比,在所有SM实例中平均。请注意,正在等待内存请求返回的变形会被记录为“正在运行”/正在飞行中。
在Nsight:Visual Studio Edition 5.5中,此度量标准显示在“管道总览汇总”中的“范围分析器结果”中。
对于全屏四元组或计算着色器工作负载,SM应该在超过95%的工作负载中处于活动状态。如果不是,那么很可能SM之间存在不平衡,一些SM处于空闲状态(没有活动的翘曲),而其他SM处于活动状态(至少有一个活动翘曲)。运行具有非均匀扭曲延迟的着色器时会发生这种情况。此外,具有少量小线程组的序列化调度调用不能足够宽以填充GPU上的所有SM。
如果对于任何几何渲染工作负载,SM活动百分比低于95%,您知道通过将异步计算工作与它重叠,可以在此工作负载上获得性能提升。在DX12和Vulkan上,这可以通过使用Compute-only队列来完成。请注意,即使SM活动百分比接近100%,也可以从异步计算获得加速,因为SM可能被跟踪为活动状态,但可能能够承受更多活动状态。
对于任何工作负载而言,“SM单元活动百分比”可能低于95%的另一个原因是频繁的GPU管线消耗(等待空闲命令,又名WFI),这可能是由以下原因造成的:
问题:出于着色器调度原因,HS和DS处于活动状态时进行很多状态更改会导致SM消失。
解决方案:尽量减少具有细化着色器的工作负载中的状态更改(包括资源绑定)的数量。
问题:给定队列上每批次的背靠背ResourceBarrier调用可能导致该队列上的所有GPU工作都耗尽。
解决方案:为了最大限度地减少ResourceBarrier调用的性能影响,重要的是尽量减少执行ResourceBarrier调用的帧中的位置数量。
问题:默认情况下,具有绑定无人机的后续渲染调用通常由我们的驱动程序注入的GPU WFI命令进行保守分隔,以防止任何数据危害。
解决方案:可以使用以下调用来禁用无人机相关的等待空闲命令的插入:NvAPI_D3D11_ {Begin,End} UAVOverlap或NvAPI_D3D11_BeginUAVOverlapEx。
请注意,在DX12,OpenGL和Vulkan中,无人机相关的等待空闲命令由应用程序使用API调用(ResourceBarrier,glMemoryBarrier或vkCmdPipelineBarrier)显式控制。
问题:在同一硬件队列中的Draw和Dispatch调用之间切换会导致GPU WFI执行。此外,在仅计算工作负载中执行非CS状态设置调用(例如,映射绑定到CS和图形着色器阶段的常量缓冲区)可能会导致WFI执行。
解决方案:批处理所有计算工作,并且不交叉图形和计算API调用,包括Map(WRITE_DISCARD)调用在所有着色器阶段绑定的“粘性”资源上。
频繁计算< - >图形开关在同一个DX11上下文中或在同一个DX12队列中。
在DX11上,多个渲染调用具有相同的无人机界限,并且没有提供给我们驱动程序的“无人机重叠”提示。
在DX12上,ResourceBarrier在障碍之间几乎没有任何工作。
数百在使用镶嵌着色器/或几何着色器的工作负载状态的变化(除了硫苷被创建作为直通快速硫苷,使用NVAPI为DX11和DX12,或NV_geometry_shader_passthrough为GL)。
如果SM是最高SOL单位(或者以SOL%计),我们将分析Nsight Range Profiler中“ SM问题利用率”指标的值。
SM单元是一个复杂单元,包含多个子单元,每个子单元具有相关的SOL%度量。PerfWorks库允许查询这些SM子SOL%指标之一:“sm__issue_active_per_elapsed_cycle_sol_pct”,该指标在范围分析器中名为“ SM Issue Utilizing Per Eilpsed Cycle”中公开。此度量是SM调度程序发出至少一条指令的已用周期的百分比。如果在给定的工作负载中,我们有“每个经过周期的SM问题利用率”==“SM SOL%”,那么我们知道最高SM子限制器是SM指令调度器,即工作负载是SM问题有限。
Range Profiler还在名称“ SM Issue Utilization Per Active Cycle ”中公开“sm__issue_active_per_active_cycle_sol_pct”度量标准。与以前的度量唯一的区别在于,每个活动周期1通过SM活动周期数(具有至少一个活动激活的周期)而不是经过的周期(挂钟)进行标准化。
“每个活动周期的SM问题利用率”度量标准对于确定给定的工作负载是否部分受“ SM占用率 ”(“sm__active_warps_avg_per_active_cycle”PerfWorks度量标准)限制很有用。
如果SM是最高的SOL单位,并且“ 每个活动周期的SM问题利用率 ”大于80%,那么当前工作负载主要受SM调度程序问题率的限制,因此增加SM占用率不会显着提高性能(在工作量最少不超过5%)。
在这种情况下,性能分类过程的下一步是计算出哪些指令正在使SM调度器的带宽饱和。通常,这是数学指令(FP32或整数操作数),但也可以是内存指令,如纹理提取或共享内存访问。此外,TEX指令(SRV和UAV访问)不太可能限制SM作为顶级SOL单元和SM问题利用率> 80%的工作负载的性能,除非TEX单元的SOL%值也接近于SM。
如本次GTC2013会议幻灯片15(t = 14分钟)所述,当给定的warp指令不能发出时(因为它的操作数没有准备好或者因为它需要执行的流水线子单元没有准备好 - 我们呼叫这是一个扭曲失速),那么SM指令调度器会尝试通过切换到不同的主动变形来隐藏延迟。因此,有两种方法可以帮助SM调度程序为每个SM活动周期发出更多指令:
增加SM占用率(调度程序可以切换到的活动warp的数量)和
减少SM发布延迟时间(所以经线保持停滞状态的周期更少)。
如果SM是最高SOL单位(或关闭),“ SM SOL% ”<80%,并且“ 每次活动周期的SM问题利用率 ”<60%,则增加SM入住率应该可以提高性能。
为了增加SM占用率,首先必须弄清楚限制它的因素。
像素和计算着色器最常见的SM占用限制器是着色器使用的每个线程的硬件寄存器数量。
硬件寄存器计数对最大理论占用率(每个活动周期的活动变形数)的影响可在我们的CUDA占用率计算器中找到。以下是“计算能力”6.1的理论占有率图,其中包括所有的GeForce GTX 10XX和Quadro Pxxxx GPU(GP10X):
图6. “计算能力” 的CUDA占用计算器图表6.1。
除寄存器外,以下资源还可限制Maxwell,Pascal和Volta GPU上的SM占用率:
对于图形着色器:
顶点着色器输出属性的总大小。
像素着色器输入属性的总大小。
HS,DS或GS的输入和输出属性的总大小。
对于像素着色器,像素扭曲的乱序完成(通常由于诸如动态循环或早退出分支的动态控制流)。请注意CS没有这个问题,因为CS线程组可以按任意顺序完成。请参阅我们GDC 2016演讲Gareth Thomas&Alex Dunn的“ Practical DirectX 12 ” 演讲幻灯片。
对于计算着色器:
线程组大小可以直接影响SM占用率,因为线程组的线程组以全部或全部方式在SM上启动(即,线程组的所有线程都具有必需的资源并将一起启动,或者不会启动将)。线程组大小越大,像共享内存和寄存器文件这样的资源量化越粗糙。虽然有些算法可能真的需要大型线程组,但在所有其他情况下,开发人员应尽量将线程组大小限制为64或32个线程。这是因为64或32线程的线程组为我们的着色器编译器提供了最大的灵活性,为着色器程序选择最佳可能的寄存器目标。
此外,对于具有高寄存器使用率(> = 64)以及SM(Group H MemoryLarrierWithGroupSync()中的高线程组屏障停顿时间的着色器,将线程组大小降低为32可能会产生加速比,与64相比较。 > = 64寄存器指导可确保每个SM限制的32个最大线程组(CUDA占用率计算器中与架构相关的“线程块/多处理器”限制)不会成为SM主要占用限制器。
每个线程组分配的共享内存字节总数也可以直接影响SM占用率,这可以通过将各种数字插入CUDA占用率计算器中看出。
我们关于实现占用率的 CUDA Nsight文档页面包括计算着色器的一些额外可能的SM占用限制器:
线程组内的工作负载不平衡。
启动的线程组太少。这对于图形着色器来说也是一个问题,它没有启动足够的warps来完全占用GPU Wait For Id之间的SM。
注意:实际上有两种方法可以减少线程组大小:
方法1:将线程组大小降低N倍,同时将网格发射维度增加N.参见上文。
方法2:将N> = 2个线程的工作合并到一个线程中。这允许通过寄存器在N个合并线程之间共享公共数据,或者在寄存器中执行减少操作,而不是使用原子操作符(例如HLSL中的InterlockedMin)在共享内存中执行减少操作。此外,这种方法还具有在N个合并线程中自动分摊线程组统一操作的优点。但是,应该警惕这种方法可能导致登记数量膨胀。
注意:如果您想了解某个给定工作负载的SM占用量是否主要针对某个全屏Pixel Shader或某个计算着色器的寄存器数限制,则可以执行以下操作:
在Scrubber中添加...“程序范围”,并找到您有兴趣研究的着色器程序范围。右键单击您的程序范围,启动Range Profiler并检查管道总览摘要中的“ SM占用率”值。
点击Scrubber中的“Time(ms)”行,在Range中选择一些渲染调用。将选项卡切换到API检查器,选择占用工作负载(PS或CS)中大部分周期的着色器阶段,然后单击着色器名称旁边的“统计”链接。
这会打开“Shaders”Nsight窗口(见下面的屏幕截图),其中显示了着色器的硬件寄存器数量,在“Regs”列中。请注意,您可能需要等待几秒钟才能填充着色器统计信息。
使用图6中的CUDA占用率计算器图查找与此寄存器计数相关的最大理论占用率,并将其与该范围分析器为该着色器报告的实际“SM占用率”进行比较。
如果实现的占用率远低于最大占用率,那么您知道SM占用率受限于每个线程的寄存器数量以外的其他因素。
图7. Nsight的“ Shaders View ”,显示“Regs”列中每个着色器的硬件寄存器数量。
为了减少为给定着色器分配的寄存器总数,可以查看DX着色器组件并研究着色器每个分支中使用的寄存器数量。硬件需要为最需要寄存器的分支分配寄存器,并且跳过该分支的经线以不理想的SM占用情况运行。
对于全屏通道(像素或计算着色器),解决此问题的典型方法是运行预处理,将像素分为不同的区域,并针对每个区域运行不同的着色器置换:
对于计算着色器,此SIGGRAPH 2016演示文稿介绍了一种使用DispatchIndirect调用将专用计算着色器应用于屏幕上不同图块的解决方案,每个着色器排列使用可变数量的线程块:“ Uncharted 4中的延迟照明 ” - Ramy El Garawany(淘气狗)。
对于像素着色器,可以使用不同的专业化方法:全屏模板缓冲区可以在预处理中填充以对像素进行分类。然后,通过在像素着色器执行之前(这应该由我们的驱动程序自动完成)依靠模板测试来发生多个绘制调用,并且使用模板测试来丢弃当前着色器置换没有的像素触摸。此GDC 2013演示文稿使用此方法优化MSAA延迟渲染:“ 孤岛危机3渲染技术 ” - Tiago Sousa,Carsten Wenzel,Chris Raine(Crytek)。
最后,为了更好地理解给定计算着色器的SM占用限制,您可以使用我们的CUDA占位计算器电子表格。要使用它,只需填写GPU 的CUDA计算能力以及着色器的资源使用情况(线程组大小,来自Shader视图的寄存器计数以及共享内存大小(以字节为单位))。
除了增加SM占用率外,还有另一种方法可以增加SM问题利用率:通过减少SM发布 - 停止周期的数量。这些是指令执行周期之间的SM活动周期,在此期间,由于其操作数未准备就绪或由于数据路径上的资源争用而导致该指令需要在其上执行,因此warp指令被暂停。
如果所有着色器都在做数学运算和纹理提取,并且每个活动周期的SM问题利用率很低,那么可以合理地假设大多数停顿周期都来自于纹理提取结果的依赖关系,我们经常称为“纹理延迟”。在这种情况下:
如果着色器包含一个可以在着色器编译时可以知道迭代次数的循环(可能通过每个循环次数使用不同的着色器置换),那么尝试强制FXC通过使用循环中的[unroll]循环属性来完全展开循环HLSL。
如果着色器正在执行无法完全展开的动态循环(例如光线循环循环),请尝试对纹理获取指令进行批处理,以减少TEX依赖性延迟的数量(通过将独立纹理拾取分批次编组为2到4在HLSL层面的背靠背说明)。
请参阅“优化光线行进循环”附录。
如果着色器对给定纹理的每个像素的所有MSAA子采样进行迭代,则可以在该纹理的单个TEX批处理指令中一起提取所有子采样。由于MSAA子采样在DRAM中彼此相邻存储,因此将它们一起提取可以最大化TEX命中率。
如果纹理加载是基于条件的,那么大多数时间都是真实的(例如,如果(idx
尝试通过改进TEX和L2缓存命中率来减少TEX延迟。通过调整采样模式以使相邻像素/线程获取更多相邻纹理像素,通过使用mipmap(如果适用)以及通过减小纹理尺寸和使用更紧凑的纹理格式,可以提高TEX&L2命中率。
尝试减少执行的TEX指令的数量(可能使用每个纹理指令使用分支,将其编译为TEX指令谓词,请参阅FXAA 3.11 HLSL的示例,例如:“if(!doneN)lumaEndN = FxaaLuma(...);”) 。
在这种情况下,我们遵循案例1(高问题利用率)和案例2(低问题利用率)的方法。
如果顶层SOL单元不是SM,而是其中一个内存子系统单元(TEX-L1,L2和DRAM),则可能导致性能较差的根本原因是TEX或L2缓存抖动-GPU友好的访问模式(通常,在一个warp中相邻的线程访问相距甚远的内存)。在这种情况下,顶部限制单元可能是TEX或L2,但根本原因可能在SM执行的着色器中,所以使用第6.2节中的方法(如果顶部SOL单元是SM )。
如果顶级SOL单元是DRAM,并且其SOL%值不差(> 60%),那么这个工作负载是DRAM吞吐量受限的,并且将其与另一个通道合并应该加速该帧。一个典型的例子是将伽马校正通道与另一个后处理通道合并。
如果CROP是顶级SOL单元,则可以尝试使用较小的渲染目标格式(例如R11G11B10F而不是RGBA16F),并且如果使用多个渲染目标,则可以尝试减少渲染目标的数量。此外,更积极地杀死像素着色器中的像素可能是值得的(例如,对于某些透明效果,丢弃具有小于1%不透明度的像素)。查看此博客文章,获取优化透明度渲染的更多可能策略:“ 透明度(或半透明度)渲染 ”。
如果ZROP是顶层SOL单元,则可以尝试使用较小的深度格式(例如D16代替D24X8用于阴影贴图,或用D24S8代替D32S8),以及以前后顺序绘制不透明物体,以便ZCULL (粗粒度深度测试)有机会在调用ZROP和像素着色器之前丢弃更多的像素。
如前所述,IA会执行索引缓冲区和顶点缓冲区加载,以收集顶点着色器的输入顶点。如果IA是顶级SOL单元,则可以尝试使用16位索引缓冲区而不是32位索引缓冲区。如果这不会增加IA SOL%,则可以尝试优化顶点重用和局部性的几何图形。此外,从其余属性中分离出位置流对于z-only或阴影贴图渲染可能是有益的。
为了结束,我们的SOL引导式性能分类方法针对任何给定的GPU工作负载执行以下操作:
检查“ SOL SOL%”值(5.2节和5.3节)。
如果> 80%=>(A)尝试从顶层SOL单元中取出工件(见第5.2.1节)。
如果<60%=>(B)尝试增加SOL%(5.2.2和6.1节)。
(A)和(B)都可以。
如果SM是最高SOL单位(或以SOL%计)或“ SM SOL% ”低于80%:
如果> 80%=>(C)尝试机会性地跳过指令组或考虑将某些计算移动到查找表(见第6.2.1节)。
如果<60%=>(D)尝试增加SM占有率(飞行中活动的经纱数量)并减少SM发货 - 停止周期的数量(见第6.2.2节)。
其他人同时做(C)和(D)。
检查“ 每个活动周期的SM问题利用率 ”值。
如果其他单位是顶级SOL单位,请尝试减少发送到该单位的工作量。(见第6.3节)。
要使用此方法,您只需要安装Nsight:在PC上安装Visual Studio Edition 5.5以及安装最新的可用图形驱动程序。
在GTX 1060 6GB @ 1506 Mhz上运行的HBAO +“DrawBlurXPS”全屏像素着色器工作负载在Nsight Range Profiler中具有以下指标:
顶部SOLs:SM:94.5%| TEX:94.5%| L2:37.2%| DRAM:27.6%
GPU空闲:0.0%=>不受CPU限制(参见第6.1.1节)
SM单元有效:99.5%=>无SM闲置问题(参见第6.1.2节)
SM问题使用率每循环周期:47.2%
每个活动周期的SM问题利用率:47.5%
SM占用:每个活动周期62.2个活动的经纱
TEX命中率:88.9%
二级命中率:87.3%
分析:
由于SM SOL%和TEX SOL%都非常接近(实际上相等),所以我们知道工作负载受到SM和TEX单元之间接口吞吐量的限制。
由于SM和TEX SOL%很高(94.5%),我们知道工作负载完全受SM和TEX单位的吞吐量的限制(95%与SOL%值一样高)。
由于每个活动周期的SM问题利用率远低于80%,我们知道指令调度器的带宽远未达到饱和。因此,增加SM占用率(每个活动周期的活动经纱数量)可能有助于理论上的帮助。但是由于SM SOL%(94.5%)太高,我们知道增加入住率并不会显着提高绩效。
TEX和L2的命中率很好(接近90%)。
结论:这个工作负载主要受SM-TEX接口带宽的限制,唯一可以显着加快执行速度的方法是减少执行的TEX指令的数量(例如,通过使用收集指令在一个TEX中执行4个单通道负载指令,或通过计算每个线程的多个输出值并通过寄存器共享纹理采样)。
HBAO +“DrawCoarseAOPS”工作负载在GTX 1060 @ 1506 Mhz上的Range Profiler中具有以下指标:
顶部SOLs:SM:93.4%| TEX:71.9%| L2:50.1%| DRAM:27.3%| CROP:3.6%
GPU空闲:0.0%=>不受CPU限制(请参阅第6.1.1节)
SM单元激活:99.5%=>没有SM空闲问题(参见章节6.1.2)
每个周期的SM问题利用率:93.4%
每个活动周期的SM问题利用率:95.9%
顶部SOL分析:
最上面的SOL单元是SM,它的最大吞吐量为93.4%,辅助SOL单元(TEX,L2和DRAM)比SM SOL多20%,所以我们知道工作负载主要是SM通过限制。
第二个上限是TEX单位,以最大吞吐量的71.9%运行。
现在,让我们进行以下思考实验:如果此GPU中的TEX单元以某种方式无限快速完成,则工作负载仍将受到SM SM内93%SM内发生的工作的限制。这些工作中的一部分通常是来自着色器的纹理提取,因此如果TEX单元无限快地完成并且SM最初受TEX延迟限制,SM SOL可能会增加。但是,由于95%的价值与实践中的SOL%值一样高,我们知道SM SOL%无法通过使其他单位更快(或处理更少的工作)而显着增加,因此我们知道唯一的显着加速这一工作量的方法是找出SM内部的性能限制器。
附加分析:
由于SM是最大限制因素,并且“每次经过周期的SM问题利用率”==“SM SOL%”,因此我们知道此工作负载的最大SM子SOL度量标准为SM问题利用率%,即工作负载为主要受指令发行率的限制。
由于“每次活动周期的SM问题利用率”> 80%,我们知道工作负载不受SM占用率的明显限制,也就是说,增加每个活动周期的活动warps数量并不会显着提高性能。
我们可以推断,性能不受纹理获取指令的延迟的限制,否则我们将“SM活动周期的利用率”远低于80%。
我们可以推断性能不受TEX单元的限制,否则我们会让TEX SOL%值更接近SM SOL%值。
结论:这个工作负载主要受着色器中数学指令的数量(FP32操作数和/或整数操作数和/或其他数学操作如rsqrt,pow,cos / sin等)的限制。为了加快速度,人们需要找到一种方法来以某种方式遮蔽较少的像素,或者每像素执行较少的数学指令。
现在,我们来看看https://github.com/NVIDIAGameWorks/D3DSamples中的“Advanced Motion Blur”DX11 SDK示例
默认情况下,应用程序以720p窗口模式运行。为了使它以4K全屏模式启动,我在main.cpp中进行了以下编辑:
#if 0 deviceParams.startfull-screen = false; deviceParams.backBufferWidth = 1280; deviceParams.backBufferHeight = 720;#其他 deviceParams.startfull-screen = true; deviceParams.backBufferWidth = 3840; deviceParams.backBufferHeight = 2160;#万一
接下来,我们可以在main.cpp中手动插入一个工作负载标记,它将被NSight拦截并在Range Profiler中成为“Perf Marker”范围:
如果(g_view_mode == VIEW_MODE_FINAL){ D3DPERF_BeginEvent(0x0,L“Final Pass”); // ... ctx-> Draw(6,0); D3DPERF_EndEvent(); }
最后,我们可以按照第4节中的步骤使用Nsight Range Profiler来分析此工作负载。在GTX 1060 @ 1506 Mhz上,Range Profiler提供以下指标:
顶部SOLs:SM:40.7%| TEX:39.8%| L2:36.3%| 作物:26.4%| DRAM:25.7%
GPU已用时间:0.70毫秒
GPU空闲:0.2%=>不受CPU限制(请参阅第6.1.1节)
SM单元有效:97.4%=>无SM闲置问题(请参阅第6.1.2节)
SM问题利用率每循环周期:40.7%
每个活动周期的SM问题利用率:41.8%
SM占用:每个活动周期37.0个活动经纱
TEX命中率:80.1%
二级命中率:83.0%
分析:
顶级SOL%单位是SM和TEX。
最高的SOL%值低于60%,所以此工作负载在GPU上运行效率低下。
由于“每个活动周期的SM问题利用率”远低于80%,并且TEX SOL%非常接近SM SOL%,所以工作负载受TEX延迟限制。
由于顶级SOL单位是SM,“SM SOL%”<80%和“每个活动周期的SM问题利用率”<60%(请参见第6.2.2节),因此工作负载也受SM占用限制。
TEX和L2命中率很好。
根据第6.2.2节,我们知道,对于SM作为顶级SOL单元的工作负载,“SM SOL%”<80%和“每次活动周期的SM问题利用率”<60%,性能可能受到以下限制:
由于没有足够的指令级并行性来隐藏指令等待时间,所以出现高的问题延迟时间和/或
SM入住率低(由于没有足够的活跃经线来隐藏问题摊位)。
让我们首先找出低SM占用率的主要原因。我们知道这个像素着色器有一个提前退出分支,如果分支被采用,通过输出调试颜色,我们可以看到大多数像素正在提前退出。我们也从第6.2.2节知道,乱序像素着色器翘曲完成(与其启动顺序相比)可以限制SM占用。
为了验证早退出分支实际上限制了占用率,让我们做一下简单地将它从像素着色器(ps_gather.hlsl)中移除的实验:
#if 0 //如果速度太短,我们只显示颜色纹理并退出 如果(TempNX度量新的价值旧价值新旧GPU已用时间5.05毫秒0.70毫秒 7.21x顶部SOL [0]SM:47.2%SM:40.7% 1.16x顶部SOL [1]TEX:39.2%TEX:39.8% 0.01XSM单元激活99.9%97.4% 1.03X每个经过周期的SM问题利用率47.2%40.7% 1.16x每个活动周期的SM问题利用率47.2%41.8% 1.13xSM入住率62.037.0 1.68xTEX命中率85.8%80.1% 1.07倍L2命中率95.2%83.0% 1.15倍
表1.删除了早期退出分支的新度量标准。
我们发现新的工作负载仍然受到SM占用的限制,但占用率(62.0)达到了硬件限制(64.0) - 即使在这种最大占用率的情况下,我们也看到工作负载仍然受TEX顶级SOL单元的TEX延迟限制 SM和TEX。
显然,由于取消了提前退出,工作量减慢了7倍,但这是可以的,因为我们希望以这种方式研究其性能限制器。这实际上是我们经常用来分析工作负载的一种方法,我们预计会出现多个性能问题:如果最初的问题太复杂,且SOL%s较低的根本原因不明确,则我们简化问题(在此情况下,消除低占用率的预期根本原因),并对简单问题重新进行分析,直到顶层SOL%s变好。这实际上是一个分而治之的性能调试方法。通过采取医学比喻,我们正在消除一种病理学,以便对其他病理学的分析更清楚并且没有“相声”。
注意:对于简化的问题,性能优化可能无法加速原始问题。但我们认为,即使发生这种情况,对大多数情况下简单问题的分析仍然非常值得,因为这不仅有助于我们验证我们对性能问题的理解是否正确,而且还有助于重新设计用于避免GPU性能低效的渲染算法。
结论:
此工作负载中SM占用率较差的主要原因是像素着色器提前退出。将这个像素着色器移动到计算着色器可以缓解这个问题(扭曲完成比给定线程组中的其他处理器更早完成将仍然限制SM占用),但是也会增加一些开销,用于通过无人机写入指令写出结果。
或者,使用第6.2.2节中描述的模板掩模方法在这种情况下也应该有所帮助,因为它将使全屏像素着色器仅处理复杂像素,并且所有这些像素将按照启动顺序完成。
优化1:使用R11G11B10F而不是RGBA16F
原始SDK示例应用程序使用RGBA16F纹理格式存储HDR颜色,这些颜色是由Motion Blur“Final Pass”过滤的颜色。
因为这个RGBA16F纹理的alpha通道从来没有被这个应用程序实际使用过,所以我们可以将其更改为更紧凑的R11G11B10F格式,从而生成在这种情况下看起来相同的输出图像。实现这种优化只是main.cpp中的格式更改:
CreateTextureWithViews( 设备,surface_desc->宽度,surface_desc->高度,#if 0 DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,#其他 DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT, DXGI_FORMAT_R11G11B10_FLOAT,#万一这样做可以通过提高TEX命中率来帮助减少颜色提取的TEX延迟:
度量新的价值旧值新旧GPU已用时间4.25毫秒5.05毫秒 增加19%顶部SOL [0]SM:63.7%SM:47.2% 1.35x顶部SOL [1]TEX:52.9%TEX:39.2% 1.35xSM单元激活99.9%99.9% 1.00倍每个经过周期的SM问题利用率63.7%47.2% 1.35x每个活动周期的SM问题利用率63.8%47.2% 1.35xSM入住率62.862.0 1.01xTEX命中率94.2%85.8% 1.10xL2命中率91.5%95.2% 0.96x表2.颜色格式从RGBA16F降至R11G11B10F的新指标。
优化2:循环展开
现在,让我们来看看这个全屏像素着色器的HLSL。它包含以下循环,每个循环迭代有3个纹理提取,以及一个跳过循环体的动态分支,用于某个循环迭代索引:
for(int i = 0; i使用以下命令行通过FXC运行此HLSL:
fxc / T ps_5_0 / Ges / O3 ps_gather.hlsl...在DXASM中产生以下控制流和纹理提取:
循环 itof r5.w,r4.w ge r6.x,r5.w,cb0 [21] .z breakc_nz r6.x ieq r6.x,r3.x,r4.w if_nz r6.x mov r4.w,r3.w 继续 endif ... sample_l_indexable(texture2d)(float,float,float,float)r6.zw,r6.xyxx,t2.zwxy,s1,l(0.000000) ... sample_l_indexable(texture2d)(float,float,float,float)r6.w,r6.xyxx,t1.yzwx,s1,l(0.000000) ... sample_l_indexable(texture2d)(float,float,float,float)r6.xyz,r6.xyxx,t0.xyzw,s2,l(0.000000) mad r5.xyz,r5.wwww,r6.xyzx,r5.xyzx iadd r4.w,r4.w,l(1)ENDLOOP我们知道这个工作量部分是TEX延迟有限。发生这种情况的原因是纹理提取和从属数学指令之间没有足够的指令。为了让我们的着色器编译器更好地调度纹理指令,这个循环需要展开,所以我们的编译器知道所有的纹理拾取都将被执行,并且可以决定如何调度它们(可能将多个纹理拾取组合在一起)尝试最佳用独立的数学指令覆盖纹理指令的延迟。
在这种情况下,循环迭代次数(“c_S”)必须在FXC编译时已知,而原始着色器中c_S是常量缓冲区值时不是这种情况。
“c_s”常量存储在一个常量缓冲区中,但由于该值不会逐帧变化,因此可以为不同的c_s常量生成此像素着色器的排列,或者仅使用#define对该值进行硬编码, 喜欢这个:
#if 0 float c_S;#其他 #define c_S 15 float c_S_unused;#万一现在c_S在FXC编译时已知,for()循环可以使用[unroll] HLSL关键字完全展开,如下所示:
[unroll] for(int i = 0; i度量新的价值旧值新旧GPU已用时间3.44毫秒4.25毫秒24%的收益顶部SOL [0]SM:81.2%SM:63.7% 1.27倍顶部SOL [1]TEX:81.2%TEX:52.9% 1.53xSM单元激活99.9%99.9% 1.00倍每个经过周期的SM问题利用率74.4%63.7% 1.17x每个活动周期的SM问题利用率74.5%63.8% 1.17xSM入住率45.262.8 0.72xTEX命中率96.2%94.2% 1.02XL2命中率85.6%91.5% 0.94倍 表3.使用展开循环的新度量标准。
由于寄存器压力增加,SM占用从每个活动周期的62.8到45.2有效弯曲下降:完全展开该循环导致更多的TEX指令同时执行,这会消耗更多寄存器来存储TEX结果。尽管如此,“每次活动周期的SM问题利用率”指标为74.5%,接近80%,因此有更多的主动式经纱可能会稍微提高性能,但不会超过工作负荷的10%。
总体而言,两项优化相结合,使工作负载的性能提高了47%(5.05至3.44毫秒)。并且,为了记录,在退出早期退出分支后,这些优化会使工作负载(0.75到0.62 ms)获得21%的增益。
附录:优化Ray-Marching循环
如第6.2.2节所述(“减少SM问题 - 延迟延迟”),包含TEX指令的动态循环可能受到TEX指令的延迟以及没有可以从相同的计划调度的独立指令的限制warp(没有指令级并行性),并且SM中的活动warps数量不足以完全隐藏TEX延迟。
这种情况通常发生在光线漫游动态循环中,这些循环通常用于屏幕空间反射(SSR)着色器以及光线行进的体积照明。
在HLSL中,具有提前退出的典型SSR光线前进循环如下所示:
float MinHitT = 1.0; float RayT = Jitter * Step + Step; [loop] for(int i = 0; i
通过部分展开循环2次并将纹理获取指令背靠背放置在HLSL中,独立纹理指令的批次可以并行执行,并且第二次获取的延迟可以部分由延迟隐藏的第一个。
假设NumSteps是2的倍数,那么生成的HLSL看起来像这样:
float MinHitT = 1.0; float RayT = Jitter * Step + Step; [loop] for(int i = 0; i通过在1607 Mhz的GTX 1080上以4K为单位的SSR测试应用程序进行上述优化,SSR工作负载所用的GPU时间从0.54 ms变为0.42 ms(工作负载加速29%)。通过进一步批量处理4个纹理,而不是2个,GPU时间下降到0.33 ms(工作负载加速64%:0.54 - > 0.33 ms /帧)。
全部0条评论
快来发表一下你的评论吧 !