2016-03-22 28 views
1

私はアセンブリの初心者です。アセンブリでBMPでエンコードされた画像のフィルタを実装する必要がある練習が難しいです。各ピクセルは24ビットで符号化されるので、ピクセルの各成分(青、緑、赤)は8ビットで符号化される。画像を表現するために、私はuint8_tの配列を持っています。たとえば、配列の最初のピクセルは配列[0](青のコンポーネント)、配列[1](緑のコンポーネント)、および配列[2](赤のコンポーネント)で表されます。私がしなければならないことは、各ピクセルの3つのコンポーネントの平均を見つけ出し、その平均値に各コンポーネントの値を固定するフィルタを実装することです。私の問題はその平均を計算することです。関数のシグネチャはextern size_t filter(const uint8_t* in_buf, uint32_t w, uint32_t h);です。これは私のコードです。アセンブリの3つの数字の平均

.text 
.global filter 

filter: 
    push %ebp 
    mov %esp, %ebp 
    xor %ecx, %ecx #increment variable for_width = 0 
    for_width: 
      xor %ebx, %ebx #increment variable for_height = 0 
      for_height: 
        calcul_offset: # find the position of the position of an i, j pixel 
          mov %ecx, %esi 
          mov %ebx, %edx 
          imull $3, %esi 
          imull $3, %edx 
          imull 12(%ebp), %edx #12(%ebp) = width of the image 
          add %edx, %esi 
        calcul_mean: 
          mov %esi, %edx 
          add 8(%esi), %edx 
          add 16(%esi), %edx #edx contains the sum of the 3 components 
        change_pixel: # supposing edx contains the mean of the 3 components 
          mov 8(%ebp), %esi #8(%ebp) is the adress of the array 
          mov %edx, (%esi) #blue component 
          mov %edx, 8(%esi) #green component 
          mov %edx, 16(%esi) #red component 
          #here is my problem - to divide %esi by 3 
        inc %ebx 
        cmp 16(%ebp), %ebx #16(%ebp) = height of the image 
        jle for_height 
      inc %ecx 
      cmp 12(%ebp), %ecx 
      jle for_width 
    # retour 
    ret 

私のコードはまだ完全ではなく、私はちょうどその時点で分裂を見つけようとしていることに注意してください。

ありがとうございました! Alexis

答えて

0

x86ファミリのプロセッサフ​​ァミリには、DIV命令があります。 DIV命令は、オペランドのサイズに応じて特定の入出力レジスタを使用します。 (ソース・オペランド(除数)によってRAX レジスタ(被除数)およびAXに 結果を格納:the programmer's guideから:AX、EDX:EAX、又はRDX

除算はAX、DXの値を符号なしAH:AL)、DX:AX、EDX:EAX、またはRDX:RAXレジスタ。 ソースオペランドは、汎用レジスタまたはメモリロケーションにすることができます。 この命令の動作は、オペランドサイズ (被除数/除数)に依存します。

だから、あなたのコードにフィードバックを求めているので、このコード

mov %esi, %eax 
xor %edx, %edx 
mov $3, %ecx 
div %ecx   
# the result is now in %eax 
+0

ありがとうございます!ちなみに、私のコードには、構文とレジスタの使用の点であなたに衝撃を与えるものがいくつかありますか? – Alexis

+0

@Alexis私が気づいた唯一の他のものは 'calcul_mean:'の後の行でした。その行は 'mov(%esi)、edx'であるべきだと私は思う。さもなければ、コードは私によく見えましたが、あなたがそれを試すまであなたは知らないでしょう:) – user3386109

1

を使用し、3でESIを分割する:代わりのもので、すべての反復をループカウンタを掛けるの、ポインタを歩いてアドオンを使用配列を通して。 addimulより安価な操作であるため、これはループ強度低下と呼ばれます。

強度を低下させない場合でも、mov-and-imulシーケンスは実際には愚かです。 imul -immediateは、3オペランド命令で、書込み専用の宛先です。 imul $3, %esi, %esiの場合、imul $3, %esiは単なる短縮形です。だから明らかにあなたはmov %ecx, %esiを落とすことができます。また、LEAを使用して小さな定数で乗算することもできます。これはIntelの構文(lea esi, [ecx + ecx*2])でより明白ですが、lea (%ecx, %ecx, 2), %esiがこのトリックを行います。

おそらく、最終アドレスを%esiにするために多くの作業をするのではなく、2レジスタアドレッシングモードを利用することもできます。しかし、it might be better to stick to one-register addressing modes for perf reasons on Intel SnB-family CPUs


私はあなたの値が1バイトuint8_tの色成分であると言いましたか? add 8(%esi), %edxには4Bのメモリソースオペランドがあります。あなたのコードを慎重に読んだことはありませんが、カラーコンポーネントを別々に扱いたい場合は、add 8(%esi), %dlなどが必要です。

これはSIMDベクトル化に適した候補です。


div非常に遅いです。これは、乗算およびシフトによって実際の除算を置き換えるために、おそらく高速ですlike any good compiler does:そのgodboltリンクから打ち鳴らす-3.8出力:

unsigned div3_32b(unsigned a) { return a/3; } 
     movl %edi, %ecx 
     movl $2863311531, %eax  # imm = 0xAAAAAAAB 
     imulq %rcx, %rax 
     shrq $33, %rax 
     retq 

gccのではなく、64B IMULの32ビット1オペランドMULを使用していますので、見ていますその場合は大胆なリンクで、そうでなければ64b-imulはほとんどのCPU、IIRCで高速になります。 (最適化リンクのタグのwikiを参照してください。)

unsigned char div3_8b(unsigned char a) { return a/3; } 
     imull $171, %edi, %eax 
     andl $65024, %eax   # imm = 0xFE00 
     shrl $9, %eax 
     retq 

8bのバージョンが打ち鳴らすのバグのようになります。ゼロにされ%ediの上位ビットを想定しているように見えるので、8×8の上位ビット - > 16B乗算結果が正しい。 64bit ABIはこれを保証しません。

ただし、インライン展開では問題ありません。そして、実際には、3つの8b番号が8bをオーバーフローさせる可能性があるため、32bレジスタの少なくとも9bの数字に対して機能するバージョンが必要です。