Shader中if和for的效率問題以及使用策略

2021-09-02 22:46:29 字數 2671 閱讀 5635

本文**:

先放結論:

if和for之所以比預想的要慢,是因為gpu的「進行計算的運算單元」和「執行指令的邏輯單元」並不是一對一對應的,而是一對多的關係。

當你執行一條指令的時候,並不是操作乙個畫素,而是同時操作一組畫素,並一條一條指令向下執行。

如果你的**裡出現了if,在這組畫素中,可能有條件表示式返回true的,也可能有返回false的。但只要有乙個返回true,下面那條**就必須執行。而那些不需要執行的畫素點,雖然這次並不需要計算,但也沒有其他的邏輯單元可以與它們配對工作,所以也只能幹等。

x的部分也只能等待

後面的else部分也是同理,即使只有乙個畫素需要計算,也需要占用全部資源。

所以,if的效率問題是會導致多個分支重複執行。

而for的問題也和if類似,會導致迴圈邏輯必須以這組畫素裡最大迴圈次數的那乙個為準來執行。

然而,也就僅此而已。

if的效率低,是因為它會導致多分支重複執行,而不是它自身有多慢。如果你去掉了if,但是執行的邏輯還是原本全部分支的總和,並不會有多少改善。step代替if的做法就是如此。

假如考慮是的是if本身這條指令的耗費的話。雖然if本身並不算乙個簡單指令,但如果要使用step來代替if,直譯則是這樣:fixed4 c = lerp(c * color,color,step(c.a,0.5));

這畢竟是兩條指令,未見得就比if本身要好多少。但如果在某些特定情況能省略掉那條lerp,確實有可能獲得一些優勢(比如:if (x >= 1) x +=1 --> x += step(1,x))

但是,使用if,也不是任何時候都會導致分支重複執行的。

當那組畫素內,如果所有c.a > 0.5的判斷都返回false的話,就意味著下面那條指令計算的結果全部都會拋棄。這種時候,gpu還是會正常的跳過這條語句。

所有分支都執行只是無奈之舉。只要能夠避免,自然就會避免。

在實際執行環境中,一批畫素內全部c.a <= 0.5的情況可能並不那麼常見,但只要出現一次,就會導致計算被跳過,這樣就可能比step的代替方案效率更高。所以,只有在我們基本確信這種情況99%都不會出現的情況,用step代替if才可能是一種「優化」,而優化幅度也未見得會高到能夠被結果體現出來。

但假如這種情況很常見,你將if替換成step,反而可能產生明顯的負優化。

這裡就有乙個問題:我們應該如何判斷,同一批畫素全部c.a <= 0.5的概率呢?這就需要知道,什麼才叫「同一批畫素」。

事實上,「同一批畫素」的分配方式,每種硬體的每一代都是不同的。有豎著掃的,也有和掃瞄線一樣橫著排的。移動平台由於都是tbr,還會以乙個方塊作為基礎分布。但不管怎麼樣,至少是連續的。至於這一批畫素到底有多少個……一般的說法是32,但這同樣也是不確定的。

我們能確定的是,假如是這樣一張透明

它有這麼大的體積,中間的部分還是實心的,那麼它的畫素被分配到同一批次的可能性就很高,也就邊緣會出現兩條分支都執行的情況。

但是,像文字這樣的,鏤空較多,概率就比較低,也就可以考慮用step代替if。但是並不保險,你看,其實連續不透明部分還是滿多的樣子的嘛,搞不好在某些單個批次畫素數量較少的機器上……

適合step代替if的情況,其實非常有限,大概只有網點圖能算乙個了。

而我在下圖的情景下使用step,與其說是了為避免if,不如說是為了減少指令。

去掉單字rect外的畫素

說白了,就是正確使用step代替if收益太少,錯誤使用step代替if代價太大。如果你沒有絕對的信心,不要用step代替if,尤其是取代分支下有大量複雜計算的if。

如果一定要這樣用的話,根據情況,最好做測試驗證下是否會導致效能的下降。

這裡有乙個特例

如果和if有關的引數是乙個外部變數(uniform)的話,由於在乙個pass裡它的值是一定不變的,所以同一批次下,也一定會走同一條分支,走多條分支的情況100%不會出現。這時候還使用step,就是愚蠢至極的做法。

但是,假如是這種情況,則更傾向於用shader變體的做法,用條件編譯來直接切換不同分支。

這樣可以將那條if語句本身的成本(據說算上else是4個時鐘週期),還有表示式的成本給消除掉。雖然優化幅度不大,但是在frag單元任何一條指令的節省都是值得的。

但是不做……

其實也沒什麼大礙,shader變體太多也可能存在記憶體和載入時間的問題,變體切換也有成本,要根據實際情況進行選擇了。

由MySQL中char和varchar效率想到的

一般認為空間換時間,現在磁碟又大又不值錢 當使用全表都是char這的字段的時候,那麼表屬性row format是fixed也就是靜態表,與之對應的自然就是動態表dynamic,靜態錶比動態表效率要高,主要是因為,基於兩點 1 沒有碎片,每行的長度是固定,所以在頻繁更新的場景下,尤其是某個欄位由小變大...

由MySQL中char和varchar效率想到的

一般認為空間換時間,現在磁碟又大又不值錢 當使用全表都是char這的字段的時候,那麼表屬性row format是fixed也就是靜態表,與之對應的自然就是動態表dynamic,靜態錶比動態表效率要高,主要是因為,基於兩點 1 沒有碎片,每行的長度是固定,所以在頻繁更新的場景下,尤其是某個欄位由小變大...

Shader中的結構體

cg語言中支援結構體 structure unity中的shader當然也支援了,實際上shader中的結構體宣告 使用和c 非常的類似,但是還是不同的。用法 1.結構體的宣告以關鍵字 struct 開始,然後緊跟結構體的名字,接下來是乙個 大括號,並以分號結尾 不要忘了分號 2.使用 引用結構體中...