2012-03-03 49 views
0

一個功能用於x86的代碼執行此(正只能是1至4,在編譯時未知):用於優化的ARMv5讀取1..4字節長的小端整數

static const uint32_t wordmask[] = { 
    0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu 
}; 
static inline uint32_t get_unaligned_le_x86(const void *p, uint32_t n) { 
    uint32_t ret = *(const uint32_t *)p & wordmask[n]; 
    return ret; 
} 

對於架構,沒有對齊的32位小端的負載,我有兩個變種:

static uint32_t get_unaligned_le_v1(const void *p, uint32_t n) { 
    const uint8_t *b = (const uint8_t *)p; 
    uint32_t ret; 
    ret = b[0]; 
    if (n > 1) { 
    ret |= b[1] << 8; 
    if (n > 2) { 
     ret |= b[2] << 16; 
     if (n > 3) { 
     ret |= b[3] << 24; 
     } 
    } 
    } 
    return ret; 
} 

static uint32_t get_unaligned_le_v2(const void *p, uint32_t n) { 
    const uint8_t *b = (const uint8_t *)p; 
    uint32_t ret = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); 
    ret &= wordmask[n]; 
    return ret; 
} 

這將是上讀取硬件更好的(我用QEMU發展),你可以提出一個更快的替代方案?如果速度更快,我就會使用程序集。

回答

4

ARM上的條件執行是提高性能的最佳選擇。 ARM查詢表(掩碼)肯定會比較慢。這裏是我的ARMv5實現:

// When called from C, r0 = first parameter, r1 = second parameter 
// r0-r3 and r12 can get trashed by C functions 
unaligned_read: 
    ldrb r2,[r0],#1  ; byte 0 is always read (n=1..4) 
    cmp r1,#2 
    ldrgeb r3,[r0],#1 ; byte 1, n >= 2 
    ldrgtb r12,[r0],#1 ; byte 2, n > 2 
    orrge r2,r2,r3,LSL #8 
    orrgt r2,r2,r12,LSL #16 
    cmp r1,#4 
    ldreqb r3,[r0],#1 ; byte 3, n == 4 
    movne r0,r2   ; recoup wasted cycle 
    orreq r0,r2,r3,LSL #24 
    mov pc,lr   ; or "bx lr" for thumb compatibility 

更新:要ldrgeb固定ldreqb

更新2:由過去的LDR/ORR之間插入指令

+0

我將第一行的操作碼更改爲「ldrb」,並將出口切換爲「bx lr」,以獲得拇指<->手臂兼容性,確實比任何其他版本都快得多。 – 2012-03-06 18:22:59

+0

感謝您發現錯字;我沒有對它進行測試,而是將它從頭頂打下來。很高興知道它明顯更快。我做了很多ARMv5優化,並一直看到表查找速度比x86慢。有條件的執行,桶式移位器和3個操作數指令是ARM的優勢。 – BitBank 2012-03-06 18:27:53

+0

我忘了問:我可以使用3段BSD許可證下的代碼嗎?它適用於Google的Snappy壓縮庫(它們只支持x86和ARMv7)的一個端口。 – 2012-03-06 20:30:40

0

你需要測試一下。很難說,因爲它不僅取決於處理器體系結構,還取決於編譯器,編譯標誌和目標系統。

這裏的另一個想法/把戲,可以消除分支機構和查表(未測試的代碼):

char mask1 = -(n>1); // 0 if n<=1, 0xFF otherwise 
char mask2 = -(n>2); 
char mask3 = -(n>3); 
ret = b[0]; 
ret |= (b[1] & mask1) << 8; 
ret |= (b[2] & mask2) << 16; 
ret |= (b[3] & mask3) << 24; 

注意這一點,因爲你的第二個功能,可以讀取過去您輸入的結束,這可能是可能不成問題。

我得到這個生成的代碼,這看起來並不那麼糟糕(15個指令,沒有分支,沒有表查找):

cmp r1, #2 
ldrb r2, [r0, #2] @ zero_extendqisi2 
ldrb r4, [r0, #1] @ zero_extendqisi2 
movls r2, #0 
cmp r1, #1 
ldrb ip, [r0, #0] @ zero_extendqisi2 
movls r4, #0 
mov r3, r2, asl #16 
ldrb r2, [r0, #3] @ zero_extendqisi2 
cmp r1, #3 
orr r0, r3, r4, asl #8 
orr r3, r0, ip 
movhi r1, r2 
movls r1, #0 
orr r0, r3, r1, asl #24 

另一件事我會嘗試被重寫你的第二個功能是這樣的:

if (n > 1) { 
    ret |= b[1] << 8; 
    } 
    if (n > 2) { 
    ret |= b[2] << 16; 
    } 
    if (n > 3) { 
    ret |= b[3] << 24; 
    } 

由於有一個更好的機會,編譯器將能夠使用conditional executio N代表這,比條件分支更快。

如果確實非常重要,它會盡可能快地運行,所以我會考慮將它寫入ARM程序集。

+0

GCC沒剃了另一個週期」 t本身並沒有執行條件執行,也沒有版本。 – 2012-03-04 09:19:04

+0

我確實說過「更好的機會」。我已經看到gcc爲一些代碼做了這個,你在用'-O3'嗎?什麼版本的gcc? – 2012-03-04 19:57:14

相關問題