[英]How can I strength reduce division by 2^n + 1?
我需要在代碼的熱路徑中執行一些整數除法。 我已經通過分析和循環計數確定了整數除法對我造成的損失。 我希望我能做些什么來強化將分裂降低到更便宜的東西。
在這條路徑中,我除以2 ^ n + 1,其中n是可變的。 基本上我想優化此函數以刪除除法運算符:
unsigned long compute(unsigned long a, unsigned int n)
{
return a / ((1 << n) + 1);
}
如果我除以2 ^ n,我只需用右移n替換div。 如果我用常數除法,我會讓編譯器強度減少那個特定的除法,可能會把它變成乘法和一些變化。
是否存在適用於2 ^ n + 1的類似優化?
編輯:這里可以是任意64位整數。 n只取10和25之間的幾個值。我當然可以為每個n預先計算一些值,但不能為a計算。
由於你只能將一個int
轉移到這么多的地方,你可以通過一個常數將所有這些情況選擇為幾個部分中的一個:
unsigned long compute(unsigned long a, unsigned int n)
{
// assuming a 32-bit architecture (making this work for 64-bits
// is left as an exercise for the reader):
switch (n) {
case 0: return a / ((1 << 0) + 1);
case 1: return a / ((1 << 1) + 1);
case 2: return a / ((1 << 2) + 1);
// cases 3 through 30...
case 31: return a / ((1 << 31) + 1);
}
}
所以現在每個除法都是一個常數,編譯器通常會減少到一系列乘法/移位/加法指令(如上所述)。 請參閱ac / c ++編譯器是否將兩次冪值的常量除法優化為班次? 對於deatils。
你可以用一個常數替換整數除法,乘以一個幻數和一個移位乘以(模數詞大小)。
可以針對已知常數預先計算幻數。
由於n不能采用多個值,例如0..31,因此為所有n預先計算這些幻數並“存儲”在具有32個元素的表中是“容易的”。
如果除數在編譯時是常數,那么一個好的編譯器可以計算幻數並通過乘法和移位替換整數除法。 根據圍繞性能關鍵代碼構建其余代碼的方式,您可以使用宏或內聯技巧來展開n的所有可能值,並讓編譯器完成查找幻數的工作( 類似於交換機的答案) ,但是我會把更多的代碼放在常量區域,否則它可能是一個不值得的交易 - 分支也會花費你的性能 )
詳細描述以及用於計算幻數的代碼可以在Henry S. Warren,Jr。的書“Hackers Delight”中獲得資金( 強烈推薦必須有書! )pp.180ff。
鏈接到相關章節的Google圖書:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.