1、关于GPU动态分支的相关内容关于GPU动态分支的相关内容2010年12月06 日星期一下午04:22今天在UnrealEditor中创建了一个很简单的的材质,用了If,看了下生成的代码,很震惊:half3GetMaterialDiffuseColorRaw(FMaterialPixelParametersParameters)float3Local1=(1.00000000)-UniformPixelVector_0.rgb);float2Local2=Parameters.TexCoords0.xy;float4Local3=tex2D(PixelTexture2D_0,Local2);fl
2、oat4Local4=(Local3*float4(1.00000000,1.00000000,1.00000000,1.00000000);float4Local5=(Local4+float4(0.00000000,0.00000000,0.00000000,0.00000000);float3Local6=(0.61799997)*Local5.rgb);float3Local7=(UniformPixelScalars_0.x=(0.00000000)?(UniformPixelScalars_0.x(0.00000000)?float3(0.61799997,0.00000000,0
3、.00000000):Local6):Local6);float3Local8=(Local7*Local1);returnLocal8;我本来以为会生成这样的代码:if(UniformPixelScalars_0.x0.00)retrun.elsefloat4color=tex2D(.);returnmul(.);/我以为生成这样的代码可以在某些时候跳过tex2D指令效率会更高,后来发现,在CPU上或许是这样,但是在GPU上面,就不一定是这样了。下面是一些相关资料,转载之,引以为戒,自省之:什么叫动态流/分支控制动态流控制的作用便是通过它来提升shader的执行性能。理论上来说,它可以避免执
4、行不必要的shader,同时也通过跳过执行那些不必要的指令来减低GPU用于shader运算的时间。它可以使程序设计更有弹性同时使设计师们的工作更加便利。动态分支采用类似的方法使得shader的使用范围变得更加广泛,同时也尽量的提高它的性能。举个例子来说,开发者如果想利用shader来解决一个多光源的问题,使用一般的方法他可能需要对每一个光源都进行一次shader设计。不过有了动态分支之后,开发者只需要简单的写一段shader程序然后根据该场景中光源的数量进行相应的设置便可以了。显然,这在一定程度上为程序开发节省了大量的时间。而且PixelShader3.0新增加的面寄存器,这属于浮点标量寄存器
5、,用来存储当前像素的三角形和视点之间的位置数据信息,如果面寄存器存放的值大于零,就表示这个视点所在的三角形面向视点,否则就是背向视点。利用面寄存器的这个特性,结合PixelShader3.0的动态分支,就能减少无效渲染,让GPU的资源更充分地利用,提高场景渲染性能。与此同时,在PixelShader3.0中引入了类似CPU的分支预测算法,这个设计来可以提高shaders处理速度。预测算法在多重渲染工作的情况可以猜测究竟是那一部分数据要被送往GPU进行处理,而不是将所有的数据一次送往处理,从而提高GPU的效能。不过现在业界对动态分支的作用存在两个对立的观点。从性能的角度来看,一些人认为这是一个非
6、常有用的功能。然而另外一些人则认为在分支设计思想中对数据处理的方法并不合理,因为NV40的PixelShader仍是SIMD体系,在执行动态分支的时候,各分支能获得的资源可能会有不足的可能,性能未必能提高此外,从支持的指令数方面,PixelShader3.0要比PixelShader复杂多了。PixelShader3.0中所支持最小指令数从2.0Extended的96条激增到了512条。虽然一个拥有如此多的指令的shader对提高运算速度是否有帮助仍然是有争论的问题,不过指令数量的增加无疑给使用者提供了更大弹性空间。注意,最小指令数量定义了硬件所可以适应于shader模型的指令数量,而不是一个
7、shader所必须用的最少的指令数。而且PixeShader3.0中的像素着色程序的长度不再受限制,这样程序员们就可以根据自己的需要任意加长程序长度了。从而能够实现更复杂的特效,然后更能免除在程序调用上的资源浪费,而且能够用一个程序实现原来多个程序的功能,让效率得到明显增加。而且DX9中支持Co-Issue,即在在一个Shader单元中拥有两个不同的独立指令,这些指令是针对于RGBA三原色与Alpha混合的运算,在R3xx中能做到的是以3/1的分配进行操作,即三原色为一单元,Alpha通道为一单元,而NV40则干脆除了3/1分配外,还能实现2/2的分配。不过,PixelShader3.0这些出
8、众的特性并不意味着就能提高画面的质量,只不过让游戏的开发者更加轻松而已、给予程序开发者们更多的弹性和能力。会理其实MicrosoftDirectX9API的第一个版本就支持PixelShader3.0,但我们在测试中发现GeForce6800Ultra没用运行Shaders,因此我们认为NV40中Shaders3.0的部分功能被目前的驱动程序关闭了。分支对GPU结构体系的挑战分支和循环是最基本的流程控制,也是编程中最基本的概念。我们知道CPU是为单线程程序运行加速而设计的,GPU是为并行程序设计的。首先CPU是指令并行,只有指令间并行的架构才需要分支预测,而GPU是线程间并行,指令按顺序发射,
9、没有分支预测。所以当GPU遇到分支问题时,性能会发生较大的变化,而且不同架构的性能变化幅度也不一样。GPU起初是为了3D渲染设计的,涉及到大量同类型平行数据计算,数据吞吐量很大,因此GPU内部包含了大量的核心(抑或叫处理单元)以满足这种需求;相反的,CPU设计侧重于通用应用,其中很大一部分晶体管用于逻辑控制单元和缓存,适用于繁琐的计算。例如CPU控制单元可以分支预测,推测执行,多重嵌套分支执行(if条件判断等等),而GPU计算的时候则将数据全部扔进着色器核心运算输出,GPU内没有流水线,因此无法实现乱序执行等技术,同时GPU的分支能力是靠线程挂起来实现的,换而言之是频繁的线程切换把分支延迟都给
10、掩盖了。让我们通过一段简单的程序来了解分支的概念:If(a)b=f()elseb=g()一个典型的条件分支程序示意图CPU可以容易地基于布尔变量a来求f()或g()函数。这种分支的性能特征相对容易理解:CPU内有相对较长的流水线和专门的功能单元,因此CPU能够正确地预测是否会采用一个特定的分支。如果这个预测能够成功地完成,通常分支会导致较小的损失。而GPU没有这种功能单元,但是也可以进行分支运算的操作,而且分支在遇到不同结构的流处理器体系时表现的性能也是有很大差异的。在单指令多数据流(SIMD)的结构中,单一控制部件向每条流水线分派指令,同样的指令被所有处理部件同时执行。另外一种控制结构是多指
11、令多数据流(MIMD),每条流水线都能够独立于其他流水线执行不同的程序。从GeForce6800Ultra开始,NVIDIA公司GPU内部的顶点着色器流水线使用MIMD方式控制,像素着色器流水线使用SIMD结构,而AMD则一直全部使用SIMD结构。MIMD能比较有效率地执行分支程序,而SIMD体系结构运行条件语句时会造成很低的资源利用率。不过SIMD需要硬件少,可以大量添加运算单元,这是一个优势。在每一个VLIWCore之内都有一个分支单元GPU可以通过线程分配器+各种临时资源的挂起能力来完成动态的线程分配,一旦某线程当前无法完成就把它挂起而不用等着,等它可以被完成了再送去其他单元进行处理,这是周边资源充足之后可以很轻松实现的东西。GPU内部的每个处理单元都不会闲着,处理不来马上换下一个线程,但这不是标准的乱序执行。乱序要和流水线衔接的,使流水线没有空闲是其目的。GPU中每个SP都是一个1级流水线的东西,流水线只能满载。所以CPU的乱序执行技术是流水线防欠载的手段,而GPU则是处理单元防欠载。这种宏观上的动态的线程分配和挂起能力决定了GPU可以利用计算来掩盖存储进行分支操作。