コンパイラが CPU レジスタ内のこのような構造体を関数に渡すことは意味があるのでしょうか?
-
26-09-2019 - |
質問
ある種の構造体に複数のプリミティブが含まれているが、その合計サイズが 4 バイト レジスタなどの単一の CPU レジスタのサイズ以下であるかどうかを知りたいのですが、コンパイラがそれを配置するのは意味があるのでしょうか?呼び出し先スタックにコピーを作成したりポインタを渡すのではなく、値または参照によって関数に渡すとき、および一般に次のような関数に複数のプリミティブを渡すときに、これらの 4 バイト レジスタの 1 つで使用します。 CPU レジスタに渡す配列や構造体が役に立つでしょうか?
このような構造のサンプル:
struct sample{
public:
char char1;
char char2;
};
構造体を関数に渡すサンプル:
void someFunc(const sample input){
//whatever
}
void someFunc(sample input){
//whatever
}
void someFunc(sample & input){
//whatever
}
void someFunc(const sample & input){
//whatever
}
解決
はい。多くのコンパイラには、構造体をスタックではなくレジスタで渡すように指定するために使用できる特別なキーワードまたは type 属性があります。これは、PowerPC のような多くのレジスタと深いパイプラインを備えたプロセッサでより一般的であり、値をメモリに書き込み、すぐにそれを読み戻すとパイプライン ストールが発生するアーキテクチャでは、パフォーマンスが大幅に向上する可能性があります。
通常は、ネイティブ レジスタと同じサイズの構造体にのみ使用します。特に、一度に 16 バイト以上を渡すことができるワイド SIMD レジスタを備えたプロセッサで便利です。これにより、(たとえば) 4 次元ベクトル (4 つの浮動小数点数) を 1 つのレジスタに渡すことができます。 AMDのSystem V は、これを許可する x86 ABI の例です。
別の例は、GCC の d64_abi タイプ属性です。これは、スタック上ではなく、可能な限りレジスタ上で構造体を渡すように PowerPC に指示します。(これは、 ダーウィン ABI).
typedef struct {
int a;
float f;
char c;
} __attribute__ ((d64_abi)) Thingy;
Thingy foo( Thingy t );
上記の場合、Foo への呼び出しは、Thingy をスタックに書き込んで再度読み取るのではなく、1 つの float レジスタと 2 つの int レジスタに渡します。戻り値も同様にレジスタに返されます。
あなたが言わなくてもこれを自動的に行うコンパイラーを見たことがありませんが、存在する可能性はあります。
他のヒント
このは、のアプリケーションバイナリインタフェース(ABI)のあなたの実行環境で定義されています。関数が呼び出されたとき、小さな構造体は、単一のレジスタにパックされた環境を作成するために合法的であるので、標準的には、プロセッサのレジスタについては何も言いません。
と呼ばれる関数の内部リファレンスのアドレスが取られたとき、それが参照されるオブジェクトのアドレスに解決しなければならないので、参照部のために、彼らは、非常に可能性がとにかくポインタとして渡される。
特定のアーキテクチャでは(i386のように、私はそれは古代のだけど、それは私が育ったものだ;)もっと多くを取る、それは確かにプッシュし、スタックからポップするので、レジスタにそれを渡すために理にかなっています(3との間で言いますより-6倍)CPUサイクルレジスタを通過します。そのために最適化する良い仕事をするだろう、コンパイラはとてもます。
私はそれは問題ではありません。他のアーキテクチャがあります想像することができます。レジスタは、より多くの改善をもたらす他の最適化のために使用されている場合は、それがこのためにそれらを使用しても意味がありません。
どのようなアーキテクチャあなたがターゲットに/使用している、またはあなたが一般的に求めている?
私は、彼らがstruct
sであっても、レジスタにポッドを渡しますコンパイラがあると思います。