質問
これは私が64ビットLinuxマシンで書いている関数です。
void myfunc(unsigned char* arr) //array of 8 bytes is passed by reference
{
unsigned long a = 0; //8 bytes
unsigned char* LL = (unsigned char*) &a;
LL[0] = arr[6];
LL[1] = arr[3];
LL[2] = arr[1];
LL[3] = arr[7];
LL[4] = arr[5];
LL[5] = arr[4];
LL[6] = arr[0];
LL[7] = arr[2];
}
さて、私の質問は次のとおりです。
- 変数「a」は、RAM またはキャッシュから何度もアクセスされないようにレジスタに保存されますか?
- 64 ビット アーキテクチャで作業している場合、64 ビット アーキテクチャでは関数のパラメーターがレジスタに格納されるため、「arr」配列もレジスタに格納されると想定する必要がありますか?
- ポインタ型のキャストはどのくらい効率的ですか?私の推測では、それはまったく非効率的ではないでしょうか?
ご協力いただければ幸いです。
よろしく
解決
a
アドレスを取得しているため、レジスタに保存することはできません。(valdo は、本当に賢いコンパイラーであることを正しく指摘しています できた 配列アクセスをビット演算に最適化し、そのままにするa
しかし、コンパイラがそれを行うのを見たことはありませんし、それが最終的に高速になるかどうかはわかりません)。arr
(ポインタ自体) はレジスタ (%edi
, 、amd64 上)。の 配列の内容 記憶の中にあります。- ポインタ型のキャスト それ自体で 多くの場合、コードがまったく生成されません。ただし、型キャストを使用して愚かなことを行うと、非常に非効率なコードが作成されたり、動作が未定義のコードが作成されたりする可能性があります。
配列内のバイトを並べ替えて数値に押し込もうとしているようですが、サンプルが生成するマシンコードはそれに関してはそれほど悪くありません。代わりにシフト操作とマスク操作を使用するという David の提案は良いものです (これにより、コードをビッグ エンディアン マシンで実行する必要がある場合にも問題が回避されます)。また、SSE ベクトル置換命令もありますが、親切だと聞いています。使うのが苦痛。
ちなみに、サンプル関数の戻り値の型は次のようにする必要があります。 unsigned long
そして、置きます return a;
最後の最後に。それなら使えます gcc -O2 -S
そしてコンパイルから何が得られるかを正確に確認してください。変更なしで戻る a
, GCC は、外部から目に見える副作用がないため、関数の本体全体を積極的に最適化します。
他のヒント
あなたは、明示的なシフトを使用することをお勧めしませんし、これを実現する命令をマスク、代わりに、配列のインデックスを使用してのかもしれません。
一般的に「レジスタAの3バイト目から負荷8ビット」のようなものを行う命令が存在しないため、配列操作は、このために使用レジスタへのコンパイラのが難しくしようとしています。 (Anは、コンパイラの最適化の可能性のそれはシフト/マスクでこれを行うことは可能だが、私はどのように可能性があることはよく分からないことを図)。
変数が
a
レジスタに格納されるかどうかは最適化の問題です。無いからvolatile
修飾子 私の意見では、スマートなコンパイラーがこれを実行します。それは呼び出し規約の問題です。慣例により、単一のポインター パラメーターがレジスター内で転送される場合、そのようになります。
arr
.ポインタ型のキャストは、CPU が解釈する操作ではありません。それ用に生成されたコードはありません。これは、何を意味するのかについてのコンパイラへの単なる情報です。
(実際には、キャストによって余分なコードが生成されることがありますが、これは多重継承とポリモーフィズムに関連しています)