[英]Does C++ compiler recognizes powers of 2?
我正在構建一個自定義哈希,我根據公式對字符串中的所有字母求和:
string[0] * 65536 + string[1] * 32768 + string[2] * 16384 + ...
而且我遇到了一個問題,我是否應該將這些數字定義為int數組中的常量,如下所示:
const int MULTIPLICATION[] = {
65536,
32768,
16384,
8192,
4096,
2048,
1024,
512,
256,
128,
64,
32,
16,
8,
4,
2,
1
}
或者,也許我應該在計算哈希本身時生成這些數字(雖然由於它們尚未生成而可能會失去一些速度)? 我需要數百萬次這個哈希計數,我希望編譯器理解的主要事情是,而不是正常的MUL操作
MOV EBX, 8
MUL EBX
它會的
SHL EAX, 3
編譯器是否理解如果我乘以2的冪來移位而不是通常的乘法?
另一個問題,我很確定當你用c ++編號* = 2時,它會移位。 但只是為了澄清,是嗎?
謝謝,我已經找到了如何在調試器中查看dissasembly。 是的,如果您使用它,編譯器確實理解移位
number *= 65536
但是,如果你這樣做,它會進行正常的乘法運算
number1 = 65536
number *= number1;
試試吧!
你用的是什么編譯器? 您可以告訴大多數編譯器在編譯后保留中間文件,或者只編譯(而不是匯編),這樣您就可以實際查看它生成的匯編代碼。
你可以看到我的另一個問題 ,這就是我所做的。
例如,在gcc中, -S
標志表示“僅編譯”。 並且-masm=intel
生成更可讀的程序集,IMO。
編輯
總而言之,我認為以下是您正在尋找的算法(未經測試):
// Rotate right by n bits
#define ROR(a, n) ((a >> n) | (a << (sizeof(a)*8-n)))
int custom_hash(const char* str, int len) {
int hash = 0;
int mult = 0x10000; // 65536, but more obvious
for (int i=0; i<len; i++) {
hash += str[i] * mult;
mult = ROR(mult, 1);
}
return mult;
}
首先,你沒有指定當你有超過16個字符時會發生什么(乘數是多少?)所以在這個實現中,我使用了一個按位旋轉。 x86具有按位旋轉指令 ( ror
和rol
用於向右和向左旋轉)。 但是,C沒有提供表達旋轉操作的方法。 所以我定義了為你做旋轉的ROR
宏。 (了解它的工作原理留給讀者練習!)
在我的循環中,我像你一樣在0x10000(65536)開始乘數。 循環的每次迭代,我將乘數右旋一位。 這基本上將它除以2,直到達到1,之后變為0x80000000。
答案取決於您的編譯器,硬件架構以及其他可能的東西。
先驗的是,用移位替換這種乘法是最佳的事情。 我認為通常應該讓編譯器進行指令級優化。
那就是說,讓我們看看我的編譯器做了什么:)
int i, j;
int main() {
j = i * 8;
}
這是使用gcc 4.7.2
和-O3
編譯的結果
_main:
LFB0:
movq _i@GOTPCREL(%rip), %rax
movl (%rax), %edx
movq _j@GOTPCREL(%rip), %rax
sall $3, %edx ;<<<<<<<<<< THE SHIFT INSTRUCTION
movl %edx, (%rax)
ret
所以,在我的環境中,答案顯然是“是”。
至於您的其他問題,請不要預先計算MULTIPLICATION
。 獲得系數
string[0] * 65536 + string[1] * 32768 + string[2] * 16384 + ...
剛開始使用coeff = 65536
並在每次迭代時將其向右移一位:
coeff >>= 1;
你為什么不只使用C ++內置的shift運算符?
(string[0] << 16) + (string[1] << 15) + (string[2] << 14) + ...
您可以使用模板元編程 ,確保在編譯時計算2的冪,而不管編譯器如何:
template<unsigned int SHIFT>
struct PowerOf2
{
static const size_t value = 1 << SHIFT;
};
為方便使用宏如下:
#define CONSTRUCT(I) (string[I] * PowerOf2<16 - I>::value)
現在用,
CONSTRUCT(0)
相當於:
string[0] * 65536
您可以通過不斷乘以2來累積它。
int doubleRunningTotalAndAdd(int runningTotal, unsigned char c)
{
runningTotal *= 2;
runningTotal += c;
return runningTotal;
}
string s = "hello";
int total = accumulate(s.rbegin(), s.rend(), 0, doubleRunningTotalAndAdd);
沒有規則; 編譯器將生成能夠提供正確結果的代碼。 我知道的所有編譯器都會使用移位組合,並在最快的解決方案時添加和減少。 我曾經研究過整數乘法比移位快的系統; 我還參與了一個系統,在這個系統中,編譯器為h * 127
生成了比(h << 7) - h
更好的代碼,盡管機器沒有硬件乘法。
如果你想將數字作為const數組的初始化器,當然,顯而易見的答案是用其他程序生成它們,並插入生成的文本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.