位反轉的最佳演算法(C語言實現)

kimylrong發表於2014-06-18

green_t 提問:

實現如下轉換的最佳演算法是什麼?

0010 0000 => 0000 0100

具體的轉換是從MSB->LSB 到 LSB->MSB,所有的位都必須反轉,那意味著,這並不是位元組順序的交換。

LSB(Least Significant Bit),意為最低有效位;MSB(Most Significant Bit),意為最高有效位。

 

最佳答案(來自Matt J

注意:下面的演算法都用C實現,但應該可以遷移到其它語言(只是不那麼快的時候可別找我)。

可選方案

記憶體佔用少(32位int,32位機器)(來源於這裡)

最快(查詢表)

來自於著名的Bit Twiddling Hacks page:

你可以擴充套件這個演算法到64位int的場景,或者為了更快的速度而犧牲多一些的記憶體(假設你的L1資料快取足夠大),有一個64K的查詢表且每次反轉16位。

其它方案

簡單

更快(32位處理器)

更快(64位處理器)

如果你想在32位int環境這樣做,那麼只需要把每一個byte反轉,然後再顛倒byte的次序即可。如下:

結果

我測試了兩個最有效的方案,查詢表和按位與(第一個方案)。測試機器為一臺膝上型電腦,配置為4G DDR2記憶體,2.4GHz的
雙核T7500處理器,4MB的L2快取。我使用的是gcc 4.3.2,64位Linux。OpenMP(外加GCC繫結)被用來提高計時器的排程能力。

reverse.c

reverse_lookup.c

在不同的優化級別(Optimizations),兩個方案我都嘗試了,每個級別跑3個案例,每個案例反轉
1億個隨機的無符號整數。對於查詢表方案,bitwise hacks page上面的兩種方法(Option 1 and Option 2)我都測試過。
結果如下:

按位與

查詢表(Option 1)

查詢表(Option 2)

結論

如果你比較在意效能,那麼使用查詢表Option 1(Byte的定址不出意外的慢)。如果你需要儘可能的利用完每一個Byte記憶體
(且你也在意bit反轉的效能),那麼優化後的按位與方案也還不賴。

附加說明

我知道上面的程式碼只是一個粗略的版本,非常歡迎大家提供一些優化的建議。以下是我知道的幾點:

  • 我沒有許可權訪問ICC,那可能更快些(如果你可以測試請在評論中回覆)。
  • 在一些L1快取比較大的現代機器上面,64K的查詢表可能工作得更好。
  • -mtune=native對 -O2/-O3(發生符號重定義的錯誤)無效,所以我不相信產生的程式碼是為我的微架構而優化。
  • SSE環境下應該有一種方法處理得更快。我不知道怎麼做,但又更快的記憶體複製,批量的按位與,調整的指令集,
    總是有一些手段的。
  • 我知道僅僅x86的指令集是危險的,下面是GCC在-O3環境產生的程式碼,所以比我更厲害的大牛可以檢查一下。

32-bit

更改: 我也嘗試在自己機器上使用uint64,看看是否效能有所提高。相對於32-bit效能大概提高了10%。
無論你是每次用64-bit型別去反轉2個32-bit的int,或者實際上看作64-bit並分兩次來反轉,效能都大致相當。
程式碼如下(對於前者,每次反轉2個32-bit的int):

相關文章