2009-08-16 17 views
2

の高速飽和およびシフト2つのハーフワード32ビットワードに2つの符号付き16ビット値があり、一定値(1から6まで可能)で右シフト(分周)する必要があります。バイト(0..0xFF)に飽和します。 = 5シフト0X 0000 0005にならなければならないと例えばARMのasm

  • 0X FFE1 00AA
  • の0x 2345 12340xの00FF 0091

私は、同時にこの擬似コードのようなものの値を飽和させるためにしようとしているになっている必要があります。私は取得

AND RT, R0, 0x80008000; - mask high bits to get negatives 
ORR RT, RT, LSR #1 
ORR RT, RT, LSR #2 
ORR RT, RT, LSR #4 
ORR RT, RT, LSR #8; - now its expanded signs in each halfword 
MVN RT, RT 
AND R0, RT; now negative values are zero 
; here something to saturate high overflow and shift after 

が、コードを非常に醜いと遅い。 :)私が今持っている 最高(最速)のものは、このような各半分の別の飽和、次のとおりです。

MOV RT, R0, LSL #16 
MOVS RT, RT, ASR #16+5 
MOVMI RT, #0 
CMP RT, RT, #256 
MOVCS RT, #255 
MOVS R0, R0, ASR #16+5 
MOVMI R0, #0 
CMP R0, R0, #256 
MOVCS R0, #255 
ORR R0, RT, R0, LSL #16 

しかし、それは10サイクルです。その後、私はこのためUSAT16命令を見つけましたが、それは、ARMv6のためだけだと、私はARMv5TE以降とARMv4の上で動作するコードが必要


編集:?:(はそれが

PSが高速になります。。:今私は私の最初のコードの書き換え:。

ANDS RT, 0x10000, R0 << 1;  // 0x10000 is in register. Sign (HI) moves to C flag, Sign (LO) is masked 
SUBNE RT, RT, 1;   // Mask LO with 0xFFFF if it's negative 
SUBCS RT, RT, 0x10000;  // Mask HI with 0xFFFF if it's negative 
BIC R0, R0, RT;   // Negatives are 0 now. The mask can be used as XOR too 
TST R0, 0xE0000000;   // check HI overflow    
ORRNE R0, R0, 0x1FE00000  // set HI to 0xFF (shifted) if so 
TST R0, 0x0000E000   // check LO overflow    
ORRNE R0, R0, 0x00001FE0  // set LO to 0xFF if so   
AND R0, 0x00FF00FF, R0 >> 5; // 0x00FF00FF is in register  

が、それは美しいではありませんが

+0

は、あなたが見て、その後Cでそれを書いて、試してみましたがどのようなコンパイラ生成する? –

+0

スティーブ、別の半分の処理をせずにC言語で書く方法がわかりません。しかし、私の脳はいくつかのアイデアを生み出しています:)その一つは "XORマスク"です。 numberがOKであれば0を含む必要があります(各半分に個別に)。 numberが負の場合は、それ自身を含みます。正のオーバフローでは、それは数値^ 0xFFFFを含むでしょう。結果はソース^マスクになります。しかし、アイデアはありません、同時にそれを行う方法 – zxcat

答えて

1

2つの操作のフラグを設定する1つのチェックを使用することをお勧めしました。しかし、私は第二の部分のためにそれをすることはできません。私は何かを行うことができます:)ここでは1から6までのいずれかのシフトで使用するユニバーサル変種です:

;prepare: 
MOV RMask, ((0xFF00 << shift) & 0xFF00) << 16; Mask overflow bits 
MOV R_0xFF00FF, 0xFF; 
ORR R_0xFF00FF, 0xFF000000; 
;... 
; innerloop: 
;.... 
TST R0, RMask, R0 << 16;   Set flags for LO half 
ORRNE R0, R0, 0xFF << shift;  It is overflow. First try positive 
BICMI R0, R0, 0xFF << shift;  Fix it if negative. LO half is ready 
TST R0, RMask, R0;    Set flags for HI half. Can TST R0, R0, #Mask also 
ORRNE R0, R0, 0xFF << (shift+16) 
BICNE R0, R0, 0xFF << (shift+16) 
AND R0, R_0xFF00FF, R0 >> shift;  Shift and mask 

だから、今7サイクルです。 :)

良いことができますか?


編集:はオーバーフローが十分にまれで、それはこのような何かを追加することをお勧めします次のようになります。

TST R0, 0xE000E000 
BEQ no_saturation_needed 
... ; saturation ops here 
1

何時間aveはあなたが述べたように問題のためにやっているのと同じくらい良いものです。タイトなループで大量のデータを処理していて、マスクを保持するレジスタをいくつか用意することができれば、1つまたは2つのサイクルを保存できるかもしれませんが、大きな改善はありません。 v6アーキテクチャより前のこの種の「小ベクトル」飽和演算は、ARMではあまりサポートされていません。

これはプログラム内の唯一のボトルネックでない限り、基本的にこれを置き去りにして次のホットスポットに移動します。

+0

はい、あなたは正しいです。私のコードは内部ループです。私はバイトではなく、バイトペアで動作するように、いくつかのデータ処理を最適化しました。すべてがうまくいっていましたが、飽和は現在インナーループの〜35%を食べています:) このタスクは面白いので、私はもっとそれを最適化しようとします。私はここのようなトリッキーなコードスニペットが好きです:http://stackoverflow.com/questions/121240/saturating-addition-in-c/121323#121323またはhttp://stackoverflow.com/questions/347889/how-to-mask- bytes-in-arm-assembly/1100352#1100352、そしておそらくこのタスクからいくつかの美しいコードを作ることができます:) – zxcat