ARM處理器NEON編程及優(yōu)化技巧—左移右移等移位操作
移位向量
NEON中的移位指令和ARM指令中的標量移位,把向量中的各個(gè)元素左移或者右移若干比特。那些移到臨近元素的比特會(huì )被丟棄掉,不會(huì )影響到鄰近元素的移位結果。移位操作的移位數可以直接編碼到指令里,或者用 一個(gè)指定的移位比特向量,如果使用移位向量,每一個(gè)元素的移位比特值將取決于對應的移位向量里存儲的值,移位向量里保存的移位值是有符號的,所以可能進(jìn)行左移,右移或者不移位的操作。
本文引用地址:http://dyxdggzs.com/article/201611/317423.htm圖1. 帶移位向量的左移操作,可以左移、右移或者不移位
有符號數據的右移操作的類(lèi)型可以根據指令來(lái)制定,如是否進(jìn)行符號擴展(算術(shù)右移還是邏輯右移),這對應于A(yíng)RM指令里的移位操作。對于無(wú)符號的右移而言,就不用進(jìn)行符號擴展了。
移位并右側插入
NEON還支持帶插入的移位,即進(jìn)行兩個(gè)向量的比特位域的組合。比如VLSI指令左移并插入,會(huì )把向量進(jìn)行左移,然后用 目標向量的右側數據來(lái)填充。如下圖所示:
arm.com/index.php?app=core&module=attach§ion=attach&attach_rel_module=blogentry&attach_id=511" rel="nofollow" >
圖2. 向量移位并填充結果向量
移位并累加
NEON支持把向量的各個(gè)元素右移然后累加結果到另外一個(gè)向量。這對于那些中間結果需要更高精度的情況非常適用,然后才把 結果保存到一個(gè)低精度的累加器里。
指令修改符
每個(gè)移位指令都能包含一個(gè)或者多個(gè)修改符,這些修改符不會(huì )改變移位操作本身,但是輸入和輸出結果會(huì )去除基準或者飽和到一個(gè)有效范圍,有5種移位限定符:
- 舍入(round),使用R前綴,用于糾正右移截斷導致的基準;
- 變窄(narrow),使用N前綴,表示向量中所有元素的位寬變窄為一半,即源是128-bit的Q寄存器,而結果是64-bit的D寄存器;
- 變長(cháng)(long),使用L前綴,表示向量中所有元素的位寬變寬為兩倍,即源是64-bit的D寄存器,而結果是128-bit的Q寄存器;
- 飽和(saturate),使用Q前綴,把結果元素變成其能表示的最大和最小值范圍內,位寬比特數和符號類(lèi)型來(lái)表明該元素的有效范圍;
- 無(wú)符號的飽和(Unsigned Saturating),使用Q前綴,U后綴,類(lèi)似于飽和限定符,但結果會(huì )飽和到無(wú)符號數據范圍,不管輸入是有符號還是無(wú)符號的;
這些限定符的有些組合起來(lái)不能描述有用的操作,因而NEON并不包含這些指令。比如類(lèi)似VQSHR的飽和右移并不需要,因為右移會(huì )讓數據變小,不會(huì )超過(guò)有效范圍。
有效的移位操作表格
所有NEON支持的移位指令如下表所示,他們按照上面提到的限定符排列。
圖3. NEON支持的移位操作,用立即數表示的移位數和用寄存器表示的
色深轉換的例子
色深轉換是圖像處理中經(jīng)常用到的。通常輸入數據是RGB565 16-bit色度格式,需要轉換成RGB888格式才更適合于NEON這種并行處理。 然而NEON還是能處理RGB565的數據的,這就需要用到前面提到的移位指令了。
圖4. RGB888和RGB565的色度格式
從RGB565到RGB888
首先看如何從RGB565轉換成RGB888,假設輸入的8個(gè)16-bit的像素保存到寄存器Q0,我們想把分量分離成R通道,G通道和B通道,保存到d2到d4寄存器。
vshr.u8 q1, q0, #3 @ 把R通道右移3比特,丟棄G通道比特
vshrn.i16 d2, q1, #5 @ 右移并變窄,取得R分量數據到d2寄存器
vshrn.i16 d3, q0, #5 @ 右移并變窄取得G分量數據
vshl.i8 d3, d3, #2 @ 左移G分量2個(gè)比特,丟棄R分量部分,同時(shí)把G分量保存到正確的位置;
vshl.i16 q0, q0, #3 @ 把B分量左移到最重要的8-bit數據
vmovn.i16 d4, q0 @ 丟地仍然有的R和G分量,保存B分量為8-bit
這些指令的含義可以參考注釋處,基本上完成的操作就是去除臨近通道的不用的色度數據,然后繼續移位把色度分量的值到最高位。
一個(gè)小問(wèn)題
你可能注意到,這樣轉換成RGB888格式后,原來(lái)的白就不是完全的白色了,這是因為R和B分量是左移3bit,而G分量則只左移兩bit,因而如RGB565值(0x1F, 0x3F, 0x1F)變成RGB888 (0xF8, 0xFC, 0xF8),并不跟以前的表示顏色一致。
從RGB888到RGB565
從RGB888轉換成RGB565,假設RGB888的輸入是用上面代碼表示的形式,單獨通道的分量保存在從寄存器d0到d2,結果保存到16-bit的RGB565格式到q2寄存器。
vshll.u8 q2, d0, #8 @ 左移紅色分量到16bit結果中的最重要的5bit
vshll.u8 q3, d1, #8 @ 左移綠色分量數據到16bit最重要的8比特
vsri.16 q2, q3, #5 @ 移位綠色分量,并插入到紅色元素寄存器里。
vshll.u8 q3, d2, #8 @ 左移紅色分量到16bit結果中的最重要的8-bit
vsri.16 q2, q3, #11 @ 把藍色分量插入到紅色和綠色分量后面
基本操作是把分量擴展成16-bit,然后右移插入指令把分量放到合適的位置;
總結
NEON提供了功能強大的移位指令,能完成:
- 快速的把數據乘以或者除以2,并帶有舍入和飽和操作;
- 移位并拷貝若干比特從一個(gè) 向量到另一個(gè)向量;
- 中間計算結果在高精度,而把結果累加到低精度。
評論