2012-03-03 13 views
0

を読み取る機能は、x86のコードはありません。この(Nのみコンパイル時に未知の1〜4であることができる):アーキテクチャについて最適化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; 
} 

その読み取りハードウェア上の方が良いだろう

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を使用しています)と、あなたがより速く、代替を提案することができます。私は2つのバリエーションを持って整列していない32ビットリトルエンディアンの負荷を持っていないのですか?それがはるかに速ければ、私はアセンブリを使用してゲームです。

答えて

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 

更新

アップデート2をldrgebする固定ldreqb:

+0

最初の行のオペコードを "ldrb"に変更し、サムの<->アーム互換性のために出口を "bx lr"に切り替えました。これは実際に他のどのバージョンよりも大幅に高速でした。 –

+0

タイプミスを発見してくれてありがとう。私はそれをテストせずに頭の上から打ちました。それがはるかに高速であることを知っておくと良いです私はARMv5の多くの最適化を行い、一貫してテーブルの参照がx86よりも遅かったことを知りました。条件付き実行、バレルシフタと3オペランド命令は、ARMの利点です。 – BitBank

+0

忘れてしまったこと:私はあなたのコードを3節BSDライセンスの下で使用してもいいですか?これは、Googleのスナッピー圧縮ライブラリ(x86とARMv7のみをサポートする)の移植用です。 –

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 

もう一つは、このようなあなたの第2の機能を書き換えることです。

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

最後のLDR/ORRの間で命令を挿入することによって、別のサイクルを削らGCCは」didnのどちらのバージョンでも、条件付き実行を単独で行うことはできません。 –

+0

私は「良いチャンス」と言った。私はgccがいくつかのコードでそれを行うのを見たことがあります、あなたは '-O3'を使用していますか?どのバージョンのgccですか? –

関連する問題