本文將介紹NEON提供的移位運(yùn)算,并顯示如何利用移位運(yùn)算在常用顏色深度之間轉(zhuǎn)換影像數(shù)據(jù)。
向量移位
NEON上的移位與標(biāo)量ARM編碼中可能用到的移位非常相似,即每個(gè)向量元素的位數(shù)均向左或向右移位,出現(xiàn)在每個(gè)元素左側(cè)或右側(cè)的位將被刪除;它們不能移位至相鄰的元素。
移位的數(shù)量可通過指令中編碼的文字或附加的移位向量來指定。使用移位向量時(shí),應(yīng)用到輸入向量每個(gè)元素的移位取決于移位向量中對(duì)應(yīng)元素的值。移位向量中的元素被當(dāng)作帶符號(hào)的值來處理,因此按元素分配,左移位、右移位和零移位都有可能發(fā)生。
帶符號(hào)元素的向量上發(fā)生的右移位由指令附加的類型指定,并會(huì)將符號(hào)擴(kuò)展至每一個(gè)元素。這與ARM編碼中可能用到的算術(shù)移位相同。應(yīng)用到無符號(hào)向量的移位不會(huì)發(fā)生符號(hào)擴(kuò)展。
移位與插入NEON也支持通過插入產(chǎn)生移位,使兩個(gè)不同向量的位相結(jié)合。例如,左移位與插入(VSLI)可使源向量的每一個(gè)元素均向左移位。每個(gè)元素右側(cè)新插入的位就是目標(biāo)向量中的對(duì)應(yīng)位。
移位與計(jì)算最后,NEON還支持向量元素向右移位,并將結(jié)果計(jì)入到另一個(gè)向量中。這種方法對(duì)于先在高精度條件下進(jìn)行臨時(shí)計(jì)算,然后再將結(jié)果與低精度計(jì)算器相結(jié)合的情況非常有用。
指令修改器每個(gè)移位指令都能擁有一個(gè)或多個(gè)修改器。這些修改器并不改變移位運(yùn)算本身,而是通過調(diào)整輸入值或輸出值,消除偏差或飽和狀況,保持一定的范圍。共有五種移位修改器:
- 舍位修改器 (Rounding),以R前綴表示,可以糾正右移時(shí)舍位導(dǎo)致的偏差。
- 窄修改器 (Narrow),以N后綴表示,可以讓結(jié)果中每個(gè)元素的位數(shù)減半。它代表Q(128位)源和D(64位)目標(biāo)寄存器。
- 長(zhǎng)修改器 (Long),以L后綴表示,可以讓結(jié)果中每個(gè)元素的位數(shù)加倍。它代表D源和Q目標(biāo)寄存器。 飽和修改器 (Saturating),以Q前綴表示,可以在最大和最小可表范圍內(nèi)設(shè)置每個(gè)結(jié)果元素,前提是結(jié)果未超出該范圍。向量的位數(shù)和符號(hào)類型可用于確定飽和范圍。
- 無符號(hào)飽和修改器 (Unsigned Saturating),以Q前綴和U后綴表示,與飽和修改器類似,但在進(jìn)行帶符號(hào)或無符號(hào)輸入時(shí),結(jié)果將在無符號(hào)范圍內(nèi)表現(xiàn)為飽和。
這些修改器的部分組合并未表現(xiàn)出有用的運(yùn)算,因此NEON也沒有提供相應(yīng)指令。例如,飽和右移位(應(yīng)稱為VQSHR)其實(shí)就毫無必要,因?yàn)橛乙莆恢粫?huì)讓結(jié)果變得更小,因而值根本無法超出有效范圍。
可用移位表NEON提供的所有移位指令均在下表中列出。它們根據(jù)先前提到的修改器進(jìn)行排列。如果你還是不太確定修改器各個(gè)字母代表的含義,請(qǐng)利用下表選擇需要的指令。
示例:轉(zhuǎn)換顏色深度顏色深度之間的轉(zhuǎn)換是圖形處理中經(jīng)常需要的運(yùn)算。通常,輸入或輸出數(shù)據(jù)都是RGB565 16位顏色格式,但RGB888格式的數(shù)據(jù)處理起來更為方便。對(duì)于NEON而言尤其如此,因?yàn)樗鼰o法為RGB565這樣的數(shù)據(jù)類型提供本機(jī)支持。
但是,NEON仍然可以有效地處理RGB565數(shù)據(jù),上文中介紹的向量移位便提供了處理方法。
從565到888首先,我們來看如何將RGB565轉(zhuǎn)換為RGB888。假設(shè)寄存器q0中有8個(gè)16位像素,我們想要在d2、d3和d4這三個(gè)寄存器中將紅色、綠色和藍(lán)色分離成8位的元素。
每個(gè)指令的效果都在上面?zhèn)渥⒅凶隽嗣枋?,但總而言之,每個(gè)通道上執(zhí)行的運(yùn)算為:
- 利用移位推掉元素任意一端的位數(shù),清除相鄰?fù)ǖ赖念伾珨?shù)據(jù)。
- 使用第二次移位將顏色數(shù)據(jù)放置到每個(gè)元素最重要的位上,并縮短位數(shù)將元素大小從16位減至8位。
請(qǐng)注意在這個(gè)順序中使用元素大小來確定8位和16位元素的位置,以進(jìn)行部分掩碼運(yùn)算。
一個(gè)小問題你可能會(huì)注意到,如果使用上述代碼轉(zhuǎn)換到RGB888格式,白色顯得不夠白。這是因?yàn)?,?duì)于每個(gè)通道而言,最低的2或3位是零,而不是1;白色在RGB565中表示為(0x1F, 0x3F, 0x1F),而在RGB888中,卻變成了 (0xF8, 0xFC, 0xF8)。這可以通過移位來解決,將部分最重要的位插入到低位。
從888到565現(xiàn)在,我們來看反向運(yùn)算,即從RGB888轉(zhuǎn)換為RGB565。這里,我們假設(shè)RGB888數(shù)據(jù)為上述代碼產(chǎn)生的格式;在d0、d1和d2這三個(gè)寄存器上,每個(gè)寄存器均包含每種顏色的8個(gè)元素。結(jié)果將存儲(chǔ)為q2格式的8個(gè)16位RGB565元素。
同樣,每個(gè)指令的詳細(xì)說明在備注中列出,但總而言之,對(duì)于每個(gè)通道而言:
- 將每個(gè)元素的長(zhǎng)度擴(kuò)展至16位,并將顏色數(shù)據(jù)移至最重要的位上。
- 使用插入右移位,將每個(gè)顏色通道放置到結(jié)果寄存器中。
結(jié)論NEON提供的強(qiáng)大的移位指令范圍讓你能夠:
- 利用舍入和飽和,通過二次冪快速進(jìn)行向量的除法和乘法運(yùn)算。
- 通過移位將一個(gè)向量位復(fù)制到另一個(gè)向量位。
- 在高精度條件下進(jìn)行臨時(shí)計(jì)算,并在低精度條件下計(jì)算結(jié)果。
Martyn是處理器領(lǐng)域的資深軟件工程師,已在ARM工作了近10年。他主要負(fù)責(zé)改善ARM平臺(tái)上軟件的性能和體驗(yàn)。他對(duì)使用匯編語言和SIMD實(shí)現(xiàn)軟件優(yōu)化非常感興趣,尤其是在圖形和多媒體領(lǐng)域。