苏州自助建站,宁波做百度网站推广,帝国cms7.0网站地图,深圳网站建设推广优化投稿人#xff1a;#xff0c;2013年6月11日这是有关GPU中浮点质量的一系列博文中的第二篇#xff0c;我的灵感源自 发表于 的文章。在中#xff0c;我宣称许多程序员其实并不真正了解浮点数字#xff0c;也指出如果您准备将它用于比较棘手的东西#xff0c;那么最好先准…投稿人2013年6月11日这是有关GPU中浮点质量的一系列博文中的第二篇我的灵感源自 发表于 的文章。在中我宣称许多程序员其实并不真正了解浮点数字也指出如果您准备将它用于比较棘手的东西那么最好先准备好更加深入地了解其运作原理甚至要超过您所希望达到的深度。我介绍了Stuart的测试也说明它披露了GPU片段着色器中所用浮点精度的位数。这很有趣但这个测试告诉我们的不止这些。在本文中我将对此进行阐述。测试与结果Stuart的测试项目使用特殊的片段着色器来计算屏幕中每一像素的灰度浓度值。作为提醒如下为我的版本。precision highpfloat;uniform vec2 resolution;voidmain( void ){floaty (gl_FragCoord.y/resolution.y) * 26.0;floatx 1.0 – (gl_FragCoord.x/resolution.x);floatbfract(pow( 2.0,floor(y) ) x);if(fract(y) 0.9)b 0.0;gl_FragColorvec4(b,b,b, 1.0 );}方框1Youi Labs GPU精度着色器(稍有修改)在上一篇博文中我已详细说明了这段代码所以这里仅总结一下该着色器绘制由26个水平条纹组成的序列。每一条纹的灰度值理想状态下是从左侧1.0(白色)到右侧0.0(黑色)的线性斜坡。不过灰度值受到了破坏首先它被加上了2B(B 是该像素所处条纹的索引)而后又被丢弃了相加结果中的整数部分。这使得每一后续条纹中的灰度值精度减少1位导致斜坡变得越来越凹凸不平。最后所有位都被丢弃条纹也变成全黑色。在Stuart的博文中他公布了这一着色器在6款移动GPU和1款高端桌面显卡上。图像的差异主要在两个方面。其一是非黑色条纹的数量如我们上次发现的该数字最终等于着色器引擎浮点有效数中小数位的数量。另一方面或许更引人注目这些条纹在屏幕上生成的图案大有不同。这就是我今天要讨论的问题。看看这些图像它们似乎分成两大阵营第一阵营由Nvidia Tegra 3、Vivante GC4000和Qualcomm Adreno 225组成它们生成的条纹一直到屏幕左边缘都是白色往右逐渐消失。其形状让我想起了虎鲸的背鳍所以我把它叫做“逆戟鲸”图案(见图1)。另一阵营由NVIDIA桌面GPU和两款ARM MaliTM设备组成它们生成对称的图案我把它叫做“蜂窝”形(见图2)。(Imagination SGX544的表现稍有不同但似乎也位列“蜂窝”阵营。)这些形状告诉我们什么一方优于另一方吗图1“逆戟鲸”图案(华为Ascend D1 / Vivante GC4000)图2“蜂窝”图案(Nexus 10 / Mali-T604)在Stuart的博文中他将许多条纹一直到屏幕左边缘都显示为白色视为良好的浮点质量。所以他非常喜欢“逆戟鲸”GPU对“蜂窝”阵营则不感冒。他尤其说道“左边缘的漂移表明计算中存在误差(本是白色的区域却为黑色)如果不加以考虑的话将转换为令人不悦的视觉差错。”他是正确的吗要得出结果我们必须探究在着色器运行时GPU的浮点单元内部会发生些什么但在这之前我们必须更进一步研究浮点的运作原理。比您真正希望的还要细致第2部分在本系列的第1部分中我快速介绍了通用单精度浮点格式其带有8位指数和24位(包括隐藏位)有效数。最后我举了个例子说明在将两个不同量度的数字相加时会发生什么例如800万加11.3125。我们从这说起(-1)0 x 222 x 1.11101000010010000000000 8000000.0(-1)0 x 23x 1.01101010000000000000000 11.3125然后对齐二进制小数点即向右偏移较小数字19位。在这之后较小数字的二进制小数点左侧不再是平常的“1”位所以我们说它被非规范化了。我们要相加的两个数字现在如下所示(-1)0 x 222 x 1.11101000010010000000000(-1)0 x 222 x 0.00000000000000000010110(1010...0)两者之和显而易见(-1)0 x 222 x 1.11101000010010000010110(1010...0) 8000011.3125请注意红色位不再能够装入有效数中。问题是我们该怎么处理它们最简单的做法是把它们丢掉在数学中这叫做向零取整(RTZ)或截尾。这相当于假装红色位全都是零尽管它们不是。将1转换为零会带来误差在本例中向零取整的结果是(-1)0 x 222 x 1.11101000010010000010110 8000011.0总误差是0.3125。想一下如果所有红色位最初都是1就会出现最糟糕的误差此时我们给有效数带来的误差是或者大约是2-23。如果愿意稍微用心一点的话我们可以做得更好。我们可以不丢弃红色位而是将它们向上或向下取整到24位有效数更接近的值。这么做被证明并不难如果第一个红色位是零我们与上述一样截尾(向下取整)。如果是1并且至少还有一个红色位是1我们向上取整。在上例中我们理想的相加结果是(-1)0 × 222 × 1.11101000010010000010110(1010...0) 8000011.3125向上取整为(-1)0 x 222 x 1.11101000010010000010111 8000011.5总误差是0.1875比向零取整的结果稍微好一点。如果第一个红色位是1而其他红色位都不是正好在两个可代表的值中间此时我们该怎么做可能有各种打破僵局的规则首选规则(也是 要求的默认规则)是朝着可以在有效数最低有效位中产生零的方向取整。这称为最近偶数取整 (RNE)。如果使用这一规则(或任何其他最近取整规则)最坏的误差是2-24而不是2-23。这似乎没多少改善但想想看使用RNE而非RTZ可将最坏的误差砍掉一半。这很了不起几乎像是免费获得了额外的一位精度。总结时间这与Stuart Russell的图像中的“逆戟鲸”和“蜂窝”又有什么关系呢他的着色器(见上图方框1)所做的大致与我们在上一小节的示例中所做的相同它将一系列逐渐变大的整数加到1.0到0.0之间的一组灰度值上导致精度误差逐渐变大。我们思考第23条纹中发生了什么在此我们要将灰度值与222相加。2的幂表示为(-1)0 x 222 x 1.00000000000000000000000 4194304.0我们在浮点数字系统中可以表示的下一个最大值是(-1)0 x 222 x 1.00000000000000000000001 4194304.5再下一个最大值是(-1)0 x 222 × 1.00000000000000000000010 4194305.0我们要与222相加的灰度值在零和1之间所以浮点单元显然要将两者之和向三个值之一取整。做完加法后着色器丢弃结果中的整数部分所以剩余的仅仅是两个可能结果之一0.0或0.5。使用RTZ的GPU始终将正数值向下取整。所以如果灰度值小于0.5结果将向下取整到4194304.0最终输出的灰度值为0.0。如果灰度值大于0.5结果将(再次向下)取整到4194304.5最终输出的灰度值为0.5。看看图1中最上方的可见条纹这就是我们所发现的现象条纹的右半部分(起始灰度值小于0.5)变为黑色右半部分(起始灰度值大于0.5)变为50%灰色。“逆戟鲸”GPU使用的是向零取整另一方面使用RNE的GPU将结果取整到它可表示的最接近值。当灰度值小于0.25时相加结果将向下取整到4194304.0因而生成黑色。当灰度值在0.25到0.75之间时相加结果将取整到4194304.5生成50%灰色。灰度值超过0.75时相加结果将向上取整到4194305.0逻辑上与白色对应但是在丢弃了结果中的整数部分时最终再次生成黑色。这就是Stuart在其博文中提到的“从左边缘漂移”也是我们在图2中看到的。“蜂窝”GPU使用的是最近取整。为了让视觉化这一点变得稍微简单一些我们可以修改着色器以便在相加结果取整到整数时保留所生成的1.0灰度值。方框2显示这一代码图3则显示了在另一个“蜂窝”GPU(AMD桌面产品(Radeon HD3650))上的运行结果。与图2相比条纹现在可以一直延伸到图像的左边缘而且出现了一个额外的第24条纹其与最近取整(似乎)给我们带来的“额外一位精度”相对应。precision highpfloat;uniform vec2 resolution;voidmain( void ){floaty (gl_FragCoord.y/resolution.y) * 26.0;floatx 1.0 – (gl_FragCoord.x/resolution.x);floatppow( 2.0,floor(y) );floatb (px) -p;if(fract(y) 0.9)b 0.0;gl_FragColorvec4(b,b,b, 1.0 );}方框2精度着色器修改后可在范围[0.0, 1.0]中生成输出看这些图片是有趣的但在本例中如果我们标绘出“逆戟鲸”和“蜂窝”GPU上部几个条纹的输入和输出灰度值则更容易发现区别。图4显示所获得的结果。(您看到的与图1和图3中的数据相同至少对于第22-24个条纹是如此–我们仅将它看作图形而不是灰度值。)我们看到了什么RNE输出和输入的近似程度要优于RTZ输出而且其平均误差为零而RTZ输出则有偏离(即平均误差不是零)。还不确信吗在图5中我标绘出了RTZ和RNE曲线中的误差–即输出与输入之间差异的绝对值。如果稍作研究在脑海中整合曲线下方的区域您会高兴(但不惊讶)地发现在平均水平上RNE方法产生的误差恰好是RTZ方法的一半。图3着色器修改后允许范围(0.0,1.0)中的灰度谁的GPU拥有质量最高的浮点单位现在我们终于可以解答这个问题Stuart图像中的图形就他所测试的GPU中的浮点质量告诉了我们些什么在他的观点中它们意味着RTZ GPU(具体而言Vivante GC4000和Qualcomm Adreno 225)生成质量最高的输出。但事实相反执行RNE取整的GPU(如)生成的结果更为准确误差更小。这也是为何最近偶数取整被指定为IEEE-754-2008中的默认取整方式。Stuart可以喜欢“逆戟鲸”图形而不是“蜂窝”但这只能基于个人喜好而不是质量。然后呢对于Stuart的着色器我真正喜欢的是它将比较难懂的浮点行为细节转换为引人注目的视觉图像。我们可否编写着色器为IEEE-754的其他死角做些类似的事可以下一次我们将窥探一下令人畏惧的Zero Hole看看一个能够表明您的GPU是否具备填补该漏洞的能力的着色器。在这之前–想要告诉问我为何定向取整确实要比最近取整好吗想要为最近奇数取整来场充满激情的辩护吗请与我联系…Tom Olson是ARM图形研究主管。在当过几年乐手(他没谈过这段经历)、多年为卫星设计数字逻辑之后他获得了博士学位并成为一名计算机视觉研究人员。大约在2001年他意识到移动设备图形显示的需求浪潮即将到来因此将自己的研究领域转向图形显示。在工作时间他经常思考ARM GPU在2013年及之后的年份将用于何种用途。在业余时间他主持Khronos OpenGL ES工作组。