gpu浮点计算能力floaps_基准测试移动 GPU 中的浮点精度 - 第 2 部分
投稿人:,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);
floatb=fract(pow( 2.0,floor(y) ) +x);
if(fract(y) >= 0.9)
b= 0.0;
gl_FragColor=vec4(b,b,b, 1.0 );
}
方框1:Youi 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);
floatp=pow( 2.0,floor(y) );
floatb= (p+x) -p;
if(fract(y) >= 0.9)
b= 0.0;
gl_FragColor=vec4(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工作組。
總結
以上是生活随笔為你收集整理的gpu浮点计算能力floaps_基准测试移动 GPU 中的浮点精度 - 第 2 部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员的4种心态与4种将来
- 下一篇: 程序员的能力拓展模型