簡體   English   中英

C ++編譯器是否識別2的冪?

[英]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具有按位旋轉指令rorrol用於向右和向左旋轉)。 但是,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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM