簡體   English   中英

優化 C 中的位譯碼操作

[英]Optimize a bit decoding operation in C

我有一個按以下方式編碼的無符號 32 位整數:

  • 前 6 位定義opcode
  • 接下來的 8 位定義了一個register
  • 接下來的 18 位是二進制補碼有符號整value

我目前正在使用以下方法解碼這個數字 (uint32_t inst):

const uint32_t opcode = ((inst >> 26) & 0x3F);
const uint32_t r1 = (inst >> 18) & 0xFF;
const int32_t value = ((inst >> 17) & 0x01) ? -(131072 - (inst & 0x1FFFF)) : (inst & 0x1FFFF);

我可以在解碼值時測量顯着的開銷,我很確定這是由於用於比較符號和執行負操作的三元運算符(本質上是一個 if 語句)造成的。

有沒有辦法以更快的方式執行值解碼?

您的表達式比它需要的更復雜,尤其是在不必要地涉及三元運算符時。 以下表達式在不涉及三元運算符的情況下為所有輸入計算相同的結果。 *這是一個很好的替代候選者,但與任何優化問題一樣,必須測試:

const int32_t value = (int32_t)(inst & 0x1FFFF) - (int32_t)(inst & 0x20000);

或者,@doynax 建議的類似變化可能對優化器更友好:

const int32_t value = (int32_t)(inst & 0x3FFFF ^ 0x20000) - (int32_t)0x20000;

在每種情況下,強制轉換都避免了實現定義的行為; 在許多架構上,就機器代碼而言,它們將是無操作的。 在這些體系結構上,這些表達式在所有情況下都比您的表達式涉及更少的操作,更不用說是無條件的了。

涉及移位的競爭替代方案也可以很好地優化,但所有這些替代方案都必須依賴於實現定義的行為,因為左移的整數溢出,負整數是右移的左手操作數,和/或轉換輸出 -范圍值到有符號整數類型。 您必須自己確定是否構成問題。

*由 GCC 4.4.7 為 x86_64 編譯。 原始表達式為某些輸入調用實現定義的行為,因此在其他實現中,這兩個表達式可能會為這些輸入計算不同的值。

標准(即使不可移植)實踐是左移后跟算術右移:

const int32_t temp = inst << 14; // "shift out" the 14 unneeded bits
const int32_t value = temp >> 14; // shift the number back; sign-extend

這涉及從uint32_tint32_t的轉換以及可能為負的int32_t的右移; 這兩個操作都是實現定義的,即不可移植(在 2 的補系統上工作;幾乎保證在任何架構上工作)。 如果您想獲得最佳性能並願意依賴實現定義的行為,則可以使用此代碼。

作為單個表達式:

const int32_t value = (int32_t)(inst << 14) >> 14;

注意:以下看起來更清晰,通常也可以工作,但涉及未定義的行為(有符號整數溢出):

const int32_t value = (int32_t)inst << 14 >> 14;

不要使用它! (即使您可能不會收到任何警告或錯誤)。

對於沒有實現定義或未定義行為的理想編譯器輸出,請使用@doynax 的 2 的補碼解碼表達式:

value = (int32_t)((inst & 0x3FFFF) ^ 0x20000) - (int32_t)0x20000;

強制轉換確保我們正在執行有符號減法,而不是無符號環繞,然后將該位模式分配給有符號整數。

這將編譯為 ARM 上的最佳 asm,其中 gcc 使用sbfx r1, r1, #0, #18 (有符號位域提取)將位 [17:0] 符號擴展為完整的int32_t寄存器。 在 x86 上,它使用shl by 14 和sar by 14(算術移位)來做同樣的事情。 這是一個明確的跡象,表明 gcc 識別 2 的補碼模式並使用目標機器上最優化的任何東西來對位域進行符號擴展。

沒有一種可移植的方式來確保位域按照您想要的方式排序。 gcc 似乎將位域從 LSB 到 MSB 排序為小端目標,但 MSB 到 LSB 用於大端目標。 您可以使用#if為帶/不帶-mbig-endian ARM 獲得相同的 asm 輸出,就像其他方法一樣,但不能保證其他編譯器的工作方式相同

如果 gcc/clang 沒有看穿 xor 和 sub,那么值得考慮<<14 / >>14實現,它讓編譯器以這種方式執行它。 或者考慮使用#if的有符號/無符號位域方法。

但既然我們可以從 gcc/clang 獲得理想的 asm 並使用完全安全和可移植的代碼,我們應該這樣做。

有關大多數答案的版本,請參閱Godbolt Compiler Explorer上的代碼。 您可以查看 x86、ARM、ARM64 或 PowerPC 的 asm 輸出。

// have to put the results somewhere, so the function doesn't optimize away
struct decode {
  //unsigned char opcode, r1;
  unsigned int opcode, r1;
  int32_t value;
};
// in real code you might return the struct by value, but there's less ABI variation when looking at the ASM this way (some would pack the struct into registers)

void decode_two_comp_doynax(struct decode *result, uint32_t inst) {
  result->opcode = ((inst >> 26) & 0x3F);
  result->r1 = (inst >> 18) & 0xFF;
  result->value = ((inst & 0x3FFFF) ^ 0x20000) - 0x20000;
}

# clang 3.7.1 -O3 -march=haswell   (enables BMI1 bextr)
    mov     eax, esi
    shr     eax, 26                     # grab the top 6 bits with a shift
    mov     dword ptr [rdi], eax
    mov     eax, 2066          # (0x812)# only AMD provides bextr r32, r32, imm.  Intel has to set up the constant separately
    bextr   eax, esi, eax               # extract the middle bitfield
    mov     dword ptr [rdi + 4], eax
    shl     esi, 14                     # <<14
    sar     esi, 14                     # >>14 (arithmetic shift)
    mov     dword ptr [rdi + 8], esi
    ret

您可以考慮使用位域來簡化您的代碼。

typedef struct inst_type {
#ifdef MY_MACHINE_NEEDS_THIS
    uint32_t opcode :  6;
    uint32_t r1     :  8;
    int32_t  value  : 18;
#else
    int32_t  value  : 18;
    uint32_t r1     :  8;
    uint32_t opcode :  6;
#endif
} inst_type;

const uint32_t opcode = inst.opcode;
const uint32_t r1 = inst.r1;
const int32_t value = inst.value;

直接位操作通常性能更好,但並非總是如此。 使用 John Bollinger 的答案作為基線,上述結構導致在 GCC 上提取三個感興趣的值的指令少了一條(但指令少並不一定意味着更快)。

const uint32_t opcode = ((inst >> 26) & 0x3F);
const uint32_t r1 = (inst >> 18) & 0xFF;
const uint32_t negative = ((inst >> 17) & 0x01);
const int32_t value =  -(negative * 131072 - (inst & 0x1FFFF));

negative為 1 -(131072 - (inst & 0x1FFFF))並且為 0 時: -(0 - (inst & 0x1FFFF))等於inst & 0x1FFFF

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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