符号なし整数と符号付き整数のパフォーマンスの違いは何ですか?[閉まっている]


42

符号付き整数とフロートを混在させると、パフォーマンスが低下することを認識しています。

unsigned intとfloatを混在させるのは悪いことですか?

符号なし/符号なしをフロートなしで混在させると何かヒットがありますか?

さまざまなサイズ(u32、u16、u8、i32、i16、i8)はパフォーマンスに影響しますか?どのプラットフォームで?


2
PS3固有のテキスト/タグを削除しました。これは、あらゆるアーキテクチャに関する良い質問であり、整数と浮動小数点レジスタを分離するすべてのアーキテクチャ(ほぼすべて)に答えが当てはまるためです。

回答:


36

int(任意の種類)とfloatの混合による大きなペナルティは、これらが異なるレジスタセットにあるためです。あるレジスタセットから別のレジスタセットに移動するには、値をメモリに書き込んでから読み戻す必要があります。これにより、load-hit-storeストールが発生します。

異なるサイズまたはintの符号付きの間を移動すると、すべてが同じレジスタセットに保持されるため、大きなペナルティを回避できます。符号拡張などにより小さなペナルティーがありますが、これらはロードヒットストアよりもはるかに小さくなります。


リンクした記事には、PS3セルプロセッサが例外であると記載されています。これは、明らかにすべてが同じレジスタセットに格納されているためです(記事のほぼ中央にあるか、「セル」を検索してください)。
bummzack

4
@bummzack:これはSPEにのみ適用され、PPEには適用されません。SPEには非常に特別な浮動小数点環境があり、キャストはまだ比較的高価です。また、符号付き整数と符号なし整数のコストは同じです。

それは良い記事であり、LHSについて知ることは重要です(そして、私はそのために投票しています)が、私の質問はそれらのサイン関連の罰則についてです。これらは小さく、おそらく無視できるものであることは知っていますが、実際の数字や参照についてはまだ見たいと思っています。
ルイス

1
@Luis-これに関する公開文書を探していましたが、現時点では見つかりませんでした。Xbox360のドキュメントにアクセスできる場合は、Bruce Dawsonによる優れたホワイトペーパーがあり、これのいくつかをカバーしています(そして一般的に非常に優れています)。
セリオン

@ルイス:私は以下の分析を投稿しましたが、それがあなたを満足させるなら、セリオンに答えを与えてください-彼が言ったことはすべて正しいです、私がしたすべてはGCCを数回実行することです。

12

Xbox 360とPS3に関する情報は、ほとんどの低レベルの詳細と同様に、ライセンスされた開発者専用の壁の背後にあると思われます。ただし、同等のx86プログラムを作成し、それを逆アセンブルして、一般的なアイデアを得ることができます。

まず、符号なしの拡張コストを見てみましょう。

unsigned char x = 1;
unsigned int y = 1;
unsigned int z;
z = x;
z = y;

関連部分は次のように分解されます(GCC 4.4.5を使用):

    z = x;
  27:   0f b6 45 ff             movzbl -0x1(%ebp),%eax
  2b:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  2e:   8b 45 f8                mov    -0x8(%ebp),%eax
  31:   89 45 f4                mov    %eax,-0xc(%ebp)

基本的に同じです-ある場合にはバイトを移動し、別の場合には単語を移動します。次:

signed char x = 1;
signed int y = 1;
signed int z;
z = x;
z = y;

に変わる:

   z = x;
  11:   0f be 45 ff             movsbl -0x1(%ebp),%eax
  15:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  18:   8b 45 f8                mov    -0x8(%ebp),%eax
  1b:   89 45 f4                mov    %eax,-0xc(%ebp)

そのため、符号拡張のコストは、サブ命令レベルではmovsblなく、どのようなコストでもmovzblあります。これは、最新のプロセッサが機能する方法のため、最新のプロセッサで定量化することは基本的に不可能です。メモリ速度からキャッシュ、事前にパイプラインにあったものまで、その他すべてがランタイムを支配します。

これらのテストを作成するのに10分ほどかかりましたが、実際のパフォーマンスバグを簡単に見つけることができました。また、コンパイラの最適化のレベルをオンにすると、そのような簡単なタスクではコードが認識できなくなります。

これはスタックオーバーフローではないので、ここで誰もマイクロ最適化が重要でないと主張しないことを願っています。ゲームは多くの場合、非常に大きく非常に数値の大きいデータで動作するため、分岐、キャスト、スケジューリング、構造のアライメントなどに注意を払うと、非常に重要な改善が得られます。PPCコードの最適化に多くの時間を費やした人は、おそらくロードヒットストアに関する少なくとも1つの恐怖物語を持っているでしょう。しかし、この場合、それは本当に重要ではありません。整数型のストレージサイズは、整列されてレジスタに収まる限り、パフォーマンスに影響しません。


2
(これは本当にセリオンの答えに対する単なるコメントであり、人々がそれをより

PS3 CPUについての情報は容易かつ合法的に入手できるため、PS3に関連するCPUの問題についての議論は問題になりません。SonyがOtherOSサポートを削除するまで、誰でもLinuxをPS3に貼り付けてプログラムできました。GPUは立ち入り禁止でしたが、CPU(SPEを含む)は問題ありません。OtherOSのサポートがなくても、適切なGCCを簡単に取得して、code-genがどのようなものかを確認できます。
ジェイソンD

@Jason:投稿にCWのフラグを付けたため、誰かがこれを行うと情報を提供できます。ただし、Sonyの公式GameOSコンパイラ(実際に重要なのは実際に唯一のもの)にアクセスできる人は誰でもアクセスできない可能性があります。

実際、符号付き整数はPPC IIRCではより高価です。小さなパフォーマンスヒットがありますが、それはあります... PS3 PPU / SPUの詳細の多くもここにあります:jheriko-rtw.blogspot.co.uk/2011/07/ps3-ppuspu-docs.htmlおよびここに:jheriko-rtw.blogspot.co.uk/2011/03/ppc-instruction-set.html。このGameOSコンパイラは何ですか?それはGCCコンパイラーですか、それともSNCですか?既に述べたもの以外のiircは、最も内側のループの最適化について話すとき、符号付き比較にオーバーヘッドがあります。私はかかわらず、これを記述したドキュメントへのアクセス権を持っていない-と私はやっていても...
jheriko

4

符号付き整数演算は、ほぼすべてのアーキテクチャでより高価になる可能性があります。たとえば、定数による除算は、符号なしの方が高速です。たとえば、

unsigned foo(unsigned a) { return a / 1024U; }

以下に最適化されます:

unsigned foo(unsigned a) { return a >> 10; }

しかし...

int foo(int a) { return a / 1024; }

最適化:

int foo(int a) {
  return (a + 1023 * (a < 0)) >> 10;
}

または分岐が安価なシステムでは、

int foo(int a) {
  if (a >= 0) return a >> 10;
  else return (a + 1023) >> 10;
}

モジュロについても同様です。これは、2のべき乗以外にも当てはまります(ただし、例はより複雑です)。アーキテクチャにハードウェア分割がない場合(たとえば、ほとんどのARM)、非定数の符号なし分割も高速です。

一般に、負の数は結果として得られないことをコンパイラーに伝えることは、式、特にループ終了やその他の条件に使用される式の最適化に役立ちます。

異なるサイズのintに関しては、はい、わずかな影響がありますが、メモリを移動することと比べて、それを比較検討する必要があります。最近では、サイズの拡張によって失われるよりも少ないメモリにアクセスする方がより多くの利益を得られるでしょう。その時点で、マイクロ最適化に非常に興味があります。


-O0であっても、GCCが実際に生成するものをより反映するように最適化されたコードを編集しました。test + leaでブランチなしで実行できる場合、ブランチがあると誤解を招きます。

2
x86では、おそらく。ARMv7では、条件付きで実行されます。
ジョンリプリー

3

符号付きまたは符号なしintを使用した操作は、現在のプロセッサ(x86_64、x86、powerpc、arm)で同じコストがかかります。32ビットプロセッサでは、u32、u16、u8、s32、s16、s8は同じでなければなりません。アラインメントが悪いとペナルティを受ける可能性があります。

ただし、intをfloatに変換するか、floatをintに変換するのはコストのかかる操作です。最適化された実装(SSE2、Neonなど)を簡単に見つけることができます。

最も重要な点は、おそらくメモリアクセスです。データがL1 / L2キャッシュに収まらない場合、変換よりも多くのサイクルを失います。


2

Jon Purdyは、上記のように(私はコメントできませんが)署名できないとオーバーフローしないため、遅くなる可能性があると言います。私は同意しません、符号なし算術は、単語のビット数に2を法とする単純なモラー演算です。原則として、署名された操作はオーバーフローする可能性がありますが、通常は無効になっています。

時々、2つ以上のデータ項目をintにパックし、命令ごとに複数の操作を取得する(ポケット算術)など、賢い(しかし非常に読みやすいものではない)ことができます。しかし、あなたはあなたが何をしているのか理解する必要があります。もちろん、MMXではこれを自然に行うことができます。ただし、サポートされる最大のハードウェアワードサイズを使用し、データを手動でパックすると、実装が最速になる場合があります。

データのアライメントに注意してください。ほとんどのHW実装では、非整列のロードとストアが遅くなります。自然なアライメントとは、たとえば4バイトのワードの場合、アドレスは4の倍数であり、8バイトのワードアドレスは8バイトの倍数であることを意味します。これはSSEに引き継がれます(128ビットは16バイトのアライメントを優先します)。AVXはまもなくこれらの「ベクトル」レジスタサイズを256ビットに拡張し、次に512ビットに拡張します。また、アライメントされたロード/ストアは、アライメントされていないロード/ストアよりも高速になります。ハードウェアオタクの場合、非境界整列メモリ操作は、キャッシュラインやページ境界などにも及ぶ可能性があります。そのため、ハードウェアは注意する必要があります。


1

符号付きオーバーフローはCでは未定義であるため、ループインデックスに符号付き整数を使用する方がわずかに優れています。そのため、コンパイラはそのようなループのコーナーケースが少ないと想定します。これは、gccの「-fstrict-overflow」(デフォルトで有効)によって制御されており、アセンブリの出力を読み取らない限り、この効果はおそらく気づきにくいでしょう。

それを超えて、x86はメモリオペランドを使用できるため、タイプを混在させない方がうまく機能します。タイプ(符号またはゼロ拡張)を変換する必要がある場合、明示的なロードとレジスタの使用を意味します。

ローカル変数にはintを使用すると、デフォルトでこれが発生します。


0

セリオンが指摘するように、intとfloatの間の変換のオーバーヘッドは、レジスタ間の値のコピーと変換に大きく関係しています。符号なし整数自体のオーバーヘッドは、保証されたラップアラウンド動作に起因するため、コンパイルされたコードで一定量のオーバーフローチェックが必要になります。

基本的に、符号付き整数と符号なし整数の間の変換にオーバーヘッドはありません。異なるサイズの整数、プラットフォームに応じて(無限に)高速または低速にアクセスできます。一般的に、プラットフォームのワードサイズに最も近い整数のサイズはアクセスが最も速くなりますが、全体的なパフォーマンスの違いは他の多くの要因、特にキャッシュサイズに依存します:uint64_t必要なときに使用する場合uint32_t、一度にキャッシュに収まるデータが少なくなり、負荷のオーバーヘッドが発生する可能性があります。

ただし、これについて考えることは少し過剰です。データに適した型を使用する場合、物事は完全にうまく機能するはずであり、アーキテクチャに基づいて型を選択することで得られるパワーの量はとにかく無視できます。


どのようなオーバーフローチェックを参照していますか?アセンブラよりも低いレベルを意味する場合を除き、2つのintを追加するコードはほとんどのシステムで同一であり、たとえば符号の大きさを使用する少数のシステムではそれほど長くありません。ただ違う。

@JoeWreschnig:くそー。私はそれを見つけることができないようですが、少なくとも特定のプラットフォームで、定義されたラップアラウンド動作の異なるアセンブラ出力アカウンティングの例を見てきたことを知っています。私は見つけることができるだけで、関連のポスト:stackoverflow.com/questions/4712315/...
ジョン・パーディ

ラップアラウンドの動作ごとに異なるアセンブラ出力は、コンパイラが符号付きのケースで最適化できるためです。たとえば、b> 0の場合、a + b> aとなります。それは本当にまったく異なる状況です。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.