質問

32ビットCPUでは、整数は4バイトで、短整数は2バイトです。指定された短整数の範囲内に常に収まる多くの数値を使用するC / C ++アプリケーションを作成している場合、4バイト整数または2バイト整数を使用する方が効率的ですか?

4バイト整数は、メモリからCPUまでのバスの帯域幅に適合するため、より効率的であると示唆されていると聞きました。ただし、2つの短い整数を加算する場合、CPUは単一のパスで両方の値を並列にパッケージ化します(したがって、バスの4バイト帯域幅にまたがります)?

役に立ちましたか?

解決

はい、32ビットCPUで32ビット整数を必ず使用する必要があります。そうしないと、未使用ビットがマスクされてしまう可能性があります(つまり、常に32ビットで計算を行い、その後、回答を16ビットに変換します)

一度に2つの16ビット操作を行うことはありませんが、コードを自分で記述してオーバーフローしないことが確実な場合は、自分で行うことができます。

編集:「効率的」の定義にもある程度依存することを付け加えます。 32ビット操作をより迅速に行うことができますが、もちろん2倍のメモリを使用します。

これらが内部ループのどこかで中間計算に使用されている場合は、32ビットを使用します。ただし、これをディスクから読み取っている場合、またはキャッシュミスの費用を支払う必要がある場合でも、16ビット整数を使用する方が適切な場合があります。すべての最適化と同様に、知る方法は1つしかありません。プロファイリング

他のヒント

数字の配列が大きい場合は、機能する最小サイズを使用します。キャッシュ密度が2倍になるため、32ビット整数よりも16ビットショートの配列を使用する方が効率的です。 CPUが32ビットレジスタの16ビット値を処理するために必要な符号拡張のコストは、キャッシュミスのコストと比べるとごくわずかです。

他のデータ型と混合したクラスで単純にメンバー変数を使用している場合、パディングの要件により16ビット値のスペース節約の利点がなくなる可能性があるため、あまり明確ではありません。

" many"を使用している場合整数値の場合、処理のボトルネックはメモリの帯域幅になりがちです。 16ビット整数はデータキャッシュにより密にパックされるため、パフォーマンスが向上します。

大量のデータを大量に処理する場合は、すべてのプログラマがすべきことを読む必要があります。 Ulrich Drepperによるメモリについて。データキャッシュの効率の最大化については、第6章に集中してください。

32ビットCPUは、通常32ビット値で内部的に動作するCPUですが、8/16ビット値で同じ動作を実行する場合に低速になるという意味ではありません。たとえば、x86は8086まで下位互換性がありますが、レジスタの一部で動作できます。つまり、レジスタの幅が32ビットであっても、そのレジスタの最初の16ビットまたは最初の8ビットでのみ動作でき、速度はまったく低下しません。この概念はx86_64でも採用されており、レジスタは64ビットですが、まだ最初の32、16、または8ビットでのみ動作できます。

また、x86 CPUは常にキャッシュにない場合は常にキャッシュライン全体をメモリからロードしますが、キャッシュラインはいずれにしても4バイトより大きい(8または16バイトではなく32ビットCPUの場合)ので、メモリから2バイトをロードするとメモリから4バイトをロードするのと同じくらい高速です。メモリから多くの値を処理する場合、16ビット値はメモリ転送が少ないため、実際には32ビット値よりもはるかに高速です。キャッシュラインが8バイトの場合、キャッシュラインごとに4つの16ビット値がありますが、32ビット値は2つしかないため、16ビットintを使用すると4つの値ごとに1つのメモリアクセスがあり、32ビットintを使用すると2つの値ごとに1つがあります、大きなint配列を処理するための転送が2倍になります。

たとえばPPCなどの他のCPUは、レジスタの一部のみを処理することはできず、常に完全なレジスタを処理します。しかし、これらのCPUには通常、特別なロード操作があります。メモリから16ビット値をロードし、32ビットに展開してレジスタに書き込みます。後で、レジスタから値を取得して最後の16ビットのみをメモリに保存する特別なストア操作があります。 32ビットのロード/ストアが必要とするように、両方の操作に必要なCPUサイクルは1つだけなので、速度の違いもありません。また、PPCはレジスタに対してのみ算術演算を実行できるため(x86はメモリを直接操作することもできます)、このロード/ストア手順は、32ビット整数を使用しても16ビット整数を使用しても実行されます。

唯一の欠点は、フルレジスタでのみ動作可能な32ビットCPUで複数の操作をチェーンする場合、最後の操作の32ビット結果を「カットバック」する必要がある場合があることです。次の操作が実行される前に16ビットになります。そうしないと、結果が正しくない場合があります。ただし、このような削減は単一のCPUサイクルのみであり(単純なAND演算)、コンパイラーはそのような削減が本当に必要な場合、およびそれを除外した場合に最終結果に影響を与えない場合の把握に非常に優れています、そのようなカットバックはすべての命令の後に実行されず、本当に避けられない場合にのみ実行されます。一部のCPUは、さまざまな「強化された」機能を提供しています。そのような削減を不要にする命令と、そのような削減を期待していたが、生成されたアセンブリコードを見て、私は人生で多くのコードを見てきましたが、コンパイラはそれを完全に回避する方法を見つけました。

したがって、ここで一般的なルールを期待している場合は、失望する必要があります。 16ビット演算が32ビット演算と同等に高速であることを確実に言うことも、32ビット演算が常に高速であると確実に言うこともできません。また、これらの数値でコードが正確に何をしているか、どのようにそれを行っているかにも依存します。特定の32ビットCPUで32ビット操作が16ビット操作を使用した同じコードよりも高速であるベンチマークを見てきましたが、すでに逆のことが当てはまりました。あるコンパイラーから別のコンパイラーに切り替えたり、コンパイラーのバージョンをアップグレードしたりしても、すべてが再び方向転換する可能性があります。私は次のことしか言うことができません:ショートパンツでの作業がintでの作業よりもかなり遅いと主張する人は、その主張のサンプルソースコードを提供し、テストに使用したCPUとコンパイラに名前を付けてください過去10年間について。いくつかの状況があるかもしれません

状況によります。 CPUに縛られている場合、32ビットCPUでの32ビット操作は16ビットより高速です。メモリバウンドの場合(特にL2キャッシュミスが多すぎる場合)、圧縮できる最小のデータを使用します。

IntelのVTune 。同じ負荷でアプリを2回実行すると、2回の実行がアプリ内のホットスポットの1つのビューにマージされ、コードの各行でその行に費やされたサイクル数を確認できます。高価なコード行で0のキャッシュミスが表示される場合、CPUに縛られています。大量のミスを見つけた場合は、メモリが不足しています。

アドバイスを聞かないで、試してください。

これはおそらく、使用しているハードウェア/コンパイラに大きく依存するでしょう。簡単なテストで、この質問を簡単に解決する必要があります。ここに質問を書くよりも、おそらくテストを書く時間が短くなります。

大規模なデータセットを操作している場合、最大の懸念事項はメモリフットプリントです。この場合の適切なモデルは、CPUが無限に高速であると想定し、メモリとの間で移動するデータの量を心配することに時間を費やすことです。実際、CPUは非常に高速であるため、データをエンコード(圧縮など)する方が効率的な場合があります。そうすれば、CPUは(潜在的に)より多くの作業(デコード/コーディング)を行いますが、メモリ帯域幅は大幅に削減されます。

したがって、データセットが大きい場合は、おそらく16ビット整数を使用することをお勧めします。リストがソートされている場合、差分またはランレングスエンコーディングを含むコーディングスキームを設計できます。これにより、メモリ帯域幅がさらに削減されます。

32ビットと言うとき、x86を意味すると仮定します。 16ビット演算は非常に低速です。オペランドサイズのプレフィックスにより、デコードが本当に遅くなります。したがって、一時変数をintまたはint16_tにしないでください。

ただし、x86は16ビットと8ビットの整数を32ビットまたは64ビットのレジスタに効率的にロードできます。 (movzx / movsx:ゼロおよび符号拡張)。したがって、配列および構造体フィールドには短整数を自由に使用できますが、一時変数には必ず整数または長整数を使用してください。

  

ただし、2つの短い整数を加算する場合、CPUは単一のパスで両方の値を並行してパッケージ化します(したがって、バスの4バイト帯域幅にまたがります)?

それはナンセンスです。ロード/ストア命令はL1キャッシュと相互作用し、制限要因はオペレーション数です。幅は関係ありません。例えばcore2:幅に関係なく、サイクルごとに1つのロードと1つのストア。 L1キャッシュには、L2キャッシュへの128または256ビットパスがあります。

負荷がボトルネックの場合、負荷後にシフトまたはマスクで分割する1つの広い負荷が役立ちます。または、SIMDを使用して、並行してロードした後、解凍せずにデータを並行して処理します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top