Neon整數除法解決方案:vrecpe_u32解析


轉載請注明出處:http://www.cnblogs.com/pepetang/p/7777243.html

上篇文章,筆者在學習使用ARM提供的Neon Intrinsics接口的過程中,代碼實現方面遇到的問題基本都能通過谷歌解決,唯獨遇到整數除法實現糾結了好長時間。一方面采用查表法對除數取其倒數的時候,由於Neon提供的查表指令限制太多,一度放棄;而針對無符號整型的近似倒數指令一直搞不清楚怎么用,為了盡快完成代碼編寫任務,當時以該指令浮點版本加上類型轉換草草收場。回過頭來進行性能優化的時候,還是想一探究竟。想起前面在ARM論壇上看到的一個關於除法實現的討論,決定參考其中一個答案試試看。該答案摘抄如下:

vrecpe.u32 takes normalized inputs, similar to how floating point significant data is usually stored. What that means is that the input has no leading zeroes past the first bit that's always 0. So the top two bits will always be 01.

Another way to look at it is that vrecpe.u32 works on values between 0.5 and 1.0 (non-inclusive), where the format is 0.1.31. That means no sign bits, 1 whole bit, and 31 fraction bits. Due to the input constraints the top bit will always be 0.

老實說,限於外語水平,當初筆者也沒完全看懂(事實上該答主的說法與實測結果也有點出入,例如輸入整型最高位實際上應該保持為1,可能是指令版本問題?),只是參考Neon匯編指令文檔知道該指令視<=0x7fffffff的整數為非法輸入,嘗試輸入0x80000000~0xffffffff的時候得到的輸出也是0x80000000~0xffffffff這個范圍。參考以下語句筆者找到了得到“normalized inputs”的方法:通過vclz指令得到一個無符號整型左移到合法輸入范圍0x80000000~0xffffffff所需要的位數。

You can find the normalization shift with a count leading zeroes instruction. In your case you'll want to use vclz.u16. But you need to leave that integer bit, so you want to set shift equal to clz(x) - 1.

接下來就是通過vrecpe得到的結果(實際上為了防止乘法溢出,應對此結果做右移處理)去做除法測試進行倒推,得到的結論是vrecpe的輸出結果是Q31的整型。以下是除法接口實現:

 0 #include "arm_neon.h"
1
/* calculate division of integers with neon intrinsics */ 2 uint32x4_t divTest(uint32x4_t y, uint32x4_t x) 3 { 4 uint32x4_t res; 5 uint32x4_t flagZero; 6 uint32x4_t shiftL, shiftR; 7 uint32x4_t x_norm, x_recp; 8 9 flagZero = vceqq_u32(x, vmovq_n_u32(0));//when the divisor is 0 10 shiftL = vclzq_u32(x); 11 shiftR = vsubq_s32(shiftL, vmovq_n_s32(40));//-[(32 - shiftL) + (31 - 23)] 12 13 x_norm = vshlq_u32(x, shiftL);//input: Q32 14 x_recp = vrecpeq_u32(x_norm);//output: Q31 15 x_recp = vshrq_n_u32(x_recp, 23);//avoid overflow 16 x_recp = vbslq_u32(flagZero, vmovq_n_u32(0), x_recp); 17 18 res = vmulq_u32(y, x_recp); 19 res = vrshlq_u32(res, shiftR); 20 21 return res; 22 }

 經測試,確實如該答主所言,這里得到的倒數精度只有8位。不過精度要求不高的話倒是可以就這么用了,缺點就是性能一般。如果有讀者了解更優實現,請留言告訴我哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM