0

C++ プログラムをアセンブリに変換するときに問題が発生しました。

ここに私のC ++コードがあります

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
  for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
    *o=*a>*x;
    *o=*b>*x|(*o<<1);
    *o=*c>*x|(*o<<1);
    *o=*d>*x|(*o<<1);
    *o=*e>*x|(*o<<1);
    *o=*f>*x|(*o<<1);
    }
}

o は出力データへのポインターであり、a、b、c、d、e、f および x は入力データへのポインターです。私が望むのは、入力データから単一の変数への比較を保存することですが、処理されるデータが大きい場合、上記のコードは効率的ではありません。プログラムは、一時データをレジスタに保存する場合と比較して、データをメモリに保存するのにより多くの時間を必要とします。

だから私がやりたいのは、このプロセスをレジスターで実行することです。私が試したのは、x によって参照されたデータを EBX に格納し、EBX を a (および b、c、d、e、f の順番で) によって参照された値を保持する ECX と比較し、比較結果を EAX に保存してシフトすることです。すべての比較が 1 つの変数に格納されるように、EAX レジスタを左に移動します。6 つの比較がすべて処理された後、ECX からの値がメモリにコピーされます。

これが私がやったことです。私のプログラムは2倍速く実行できますが、取得した値はすべてゼロです。多分私は間違った方法でそれをしますか?

      __asm__(
"xorl %%eax,%%eax;"
"xorl %%ebx,%%ebx;"
"xorl %%ecx,%%ecx;"

"movl %1, %%ebx;"

//start here
"movl %2,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .one;"
"orl $0x1,%%eax;"

".one:;"
"shll $1,%%eax;"
"movl %3,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .two;"
"orl $0x1,%%eax;"

".two:;"
"shll $1,%%eax;"
"movl %4,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .three;"
"orl $0x1,%%eax;"

".three:;"
"shll $1,%%eax;"
"movl %5,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .four;"
"orl $0x1,%%eax;"

".four:"
"shll $1,%%eax;"
"movl %6,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .five;"
"orl $0x1,%%eax;"

".five:"
"shll $1,%%eax;"
"movl %7,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .six;"
"orl $0x1,%%eax;"

".six:"
//output
"movl %%eax,%0;"

:"=r"(sett)
:"r"((int)*x),"r"((int)*a) ,"r"((int)*b) ,"r"((int)*c) ,"r"((int)*d),"r"((int)*e),"r"((int)*f) /* input */
  );
4

2 に答える 2

1

いくつかのオプション:

1) 手作りのアセンブリ コードを破棄します。あなたは C コードが遅いと言いましたが、どの程度か教えてください。asmバージョンでは正しい結果が得られないため、意味のある方法で違いを測定する方法がわかりません。別の言い方をすれば、試しasm("nop;");てみてください。誤った結果を生成するよりも高速な方法です。

2) C コードを*x1 回だけ読み取るように書き直します。結果を一時変数に保持*oし、最後にのみ書き込みます。

3) セマンティクスに適している (そしてコンパイラでサポートされている) 場合は、ポインタをrestrict/ __restrict/ __restrict__(C99 から、拡張機能として C++ で一般的に利用可能) で装飾して、コンパイラが*o.

4) コンパイラは、ループを自動的に展開するのにかなり優れています。コマンドライン オプション、#pragmaディレクティブ、または拡張機能/属性の組み合わせが必要になる場合があります。

編集

これは、一時的なものを使用するように書き直すことで意味します。

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
    for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
        uint32_t tmp_x = *x;
        *o = (*a > tmp_x ? 0x20 : 0)
          |  (*b > tmp_x ? 0x10 : 0)
          |  (*c > tmp_x ? 0x08 : 0)
          |  (*d > tmp_x ? 0x04 : 0)
          |  (*e > tmp_x ? 0x02 : 0)
          |  (*f > tmp_x ? 0x01 : 0);
    }
}

どんな違いがあるの?元のバージョンでxは、すべての課題で読み込まれます。コンパイラはそれを認識せず、別の場所ox指しています。最悪の場合、コンパイラx毎回から再度読み取る必要がありoますx

もちろん、このコードには異なるセマンティクスがあります。o他のポインターのいずれかを実際にエイリアスにする場合は、元のコードとは異なることを行います。

于 2013-08-10T04:37:57.983 に答える
0

最近の Intel チップを使用していると仮定します。...そして、あなたが本当に使いたいと思うのは、(Cray と言うのに使われている場合はかなり制限されています:-) ベクトル機能です。これらは AVX と呼ばれます。C / C ++でこれを行うライブラリもあります.AVXとCをグーグルで検索することから始めます.

そうは言っても、「register」キーワードを使用して、いくつかの変数をレジスターに格納するようにコンパイラーに指示することもできます。C++ のこの Register キーワードを参照してください。

于 2013-08-12T15:49:21.537 に答える