9

CUDA Cベストプラクティスガイドには、符号付き整数と符号なし整数の使用に関する小さなセクションがあります。

C言語標準では、符号なし整数オーバーフローのセマンティクスは明確に定義されていますが、符号付き整数オーバーフローは未定義の結果を引き起こします。したがって、コンパイラーは、符号なし演算よりも符号付き演算の方が積極的に最適化できます。これは、ループカウンターで特に注意が必要です。ループカウンターは常に正の値を持つのが一般的であるため、カウンターを符号なしとして宣言したくなる場合があります。ただし、パフォーマンスを少し向上させるには、代わりに署名済みとして宣言する必要があります。

たとえば、次のコードについて考えてみます。

    for(i = 0; i <n; i ++){  
         out [i] = in [offset + stride * i];  
    }

ここで、部分式stride*iは32ビット整数をオーバーフローする可能性があるため、iが符号なしとして宣言されている場合、オーバーフローセマンティクスにより、コンパイラは、強度低下など、他の方法で適用された可能性のあるいくつかの最適化を使用できなくなります。代わりに、オーバーフローのセマンティクスが定義されていない場合に、iが署名済みとして宣言されている場合、コンパイラーはこれらの最適化を使用する余地があります。

特に最初の2つの文は私を混乱させます。符号なしの値のセマンティクスが明確に定義されており、符号付きの値が未定義の結果を生成する可能性がある場合、コンパイラーはどのようにして後者のより良いコードを生成できますか?

4

3 に答える 3

13

テキストはこの例を示しています:

for (i = 0; i < n; i++) {  
     out[i] = in[offset + stride*i];  
}

また、「強度低下」についても言及しています。コンパイラは、これを次の「疑似最適化C」コードに置き換えることができます。

tmp = offset;
for (i = 0; i < n; i++) {  
     out[i] = in[tmp];
     tmp += stride;
}

ここで、浮動小数点数(およびサブセットとしての整数)のみをサポートするプロセッサーを想像してみてください。tmpタイプは「非常に多数」になります。

現在、C標準では、符号なしオペランドを含む計算はオーバーフローすることはなく、代わりに最大値+1を法として減少するとされています。つまり、符号なしの場合i、コンパイラは次のことを行う必要があります。

tmp = offset;
for (i = 0; i < n; i++) {  
     out[i] = in[tmp];
     tmp += stride;
     if (tmp > UINT_MAX)
     {
         tmp -= UINT_MAX + 1;
     }
}

しかし、符号付き整数の場合、コンパイラーは必要なことを何でも実行できます。オーバーフローをチェックする必要はありません。オーバーフローが発生した場合は、開発者の問題です(例外が発生したり、誤った値が生成されたりする可能性があります)。したがって、コードを高速化できます。

于 2012-12-31T21:27:05.213 に答える
2

これは、C の定義が、符号なし整数の場合にコンパイラ作成者ができることを制限しているためです。符号付き整数がオーバーフローした場合に何が起こるかをいじる余裕があります。いわば、コンパイラの作成者には、より多くの移動の余地があります。

それが私がそれを読む方法です。

于 2012-12-31T20:56:55.420 に答える
1

signedとのセマンティクスの違いは、unsignedC で定義されたすべてのワード サイズをサポートしていないプロセッサでのパフォーマンスに関係します。int(32 ビット) とchar(8 ビット*)の両方を使用する C 関数を作成します。

int test(char a) {
  char b = a * 100;
  return b;
}

CPU は 32 ビット レジスタにしか格納できずchar、32 ビット値の演算しか実行できないため、32 ビット レジスタを使用して b を保持し、32 ビット乗算演算を実行します。

C 標準では、符号付き整数のオーバーフローによって未定義の結果が生じると規定されているため、上記の関数で が 2 より大きいときに 127 より大きい値を返すコードをコンパイラが作成しても問題ありませんa

ただし、符号なしの値が使用されている場合:

unsigned int test(unsigned char a) {
  unsigned char b = a * 100;
  return b;
}

C 標準では、符号なし操作のオーバーフロー セマンティクスが定義されているため、コンパイラはマスキング操作を追加して、 が 2 より大きい場合でも関数が 255 より大きい値を返さないようにする必要がありますa


* C 仕様では 8 ビットを超える幅を使用できますが、多くのプログラムが壊れてしまうため、この例charでは 8 ビット値を使用するコンパイラを想定しています。char

于 2013-01-01T00:02:08.437 に答える