C:Memcpy対シフト:もっと効率的ですか?

I have a byte array containing 16 & 32bit data samples, and to cast them to Int16 and Int32 I currently just do a memcpy with 2 (or 4) bytes.

memcpyはおそらく2バイトの長さのために最適化されていないので、整数演算(または共用体)を使ってバイトをInt32に変換する方が効率的かどうか疑問に思っていました。

私はコードが組み込みプラットフォーム上で動作するので、memcpyとビットシフトを呼び出す効率がどのようなものかを知りたい。

3
@MatコンパイラはGCCでCPUは​​Cortex M3ですが、生成するアセンブリコードを理解することはできません。私はちょうど一般的な場合のパフォーマンスの違いを知りたいのですが、その違いが非常に小さい場合は、CPU /コンパイラに依存して、私は無視することができると思いますか?
追加された 著者 Muis,
コンパイラが生成するアセンブリを比較します。コンパイル時にコピーされたサイズが分かっている場合、 memcpy の最適化についてはかなりスマートです。
追加された 著者 Mat,
Cortex M3のアセンブリは、x86と同じくらい悪くありません。 LOAD ans STOREのような高価な命令を探します。
追加された 著者 Lindydancer,

2 答え

私は、 memcpy はこれを行う方法ではないと言います。しかし、最高の方法を見つけることは、データがメモリにどのように格納されるかによって大きく異なります。

まず、宛先変数のアドレスを取得する必要はありません。ローカル変数の場合は、コンパイラにプロセッサレジスタに配置するオプションを与えるのではなく、スタックに強制します。これだけでは非常に高価になる可能性があります。

最も一般的な解決策は、バイトごとにデータを読み取り、結果を算術的に結合することです。例えば:

uint16_t res = (  (((uint16_t)char_array[high]) << 8)
                | char_array[low]);

あなたがより多くの選択肢を持っているので、32ビットの場合の式は少し複雑です。最適なアセンブラ出力を確認することができます。

Alt 1:パリを構築し、それらを結合する:

uint16_t low16 = ... as example above ...;
uint16_t high16 = ... as example above ...;
uint32_t res = (  (((uint32_t)high16) << 16)
                | low16);

Alt 2:一度に8ビットずつシフト:

uint32_t res = char_array[i0];
res = (res << 8) | char_array[i1];
res = (res << 8) | char_array[i2];
res = (res << 8) | char_array[i3];

上記のすべての例は、インデックス値によってどの部分を読み込むかを決めるため、使用するプロセッサのエンディアンに中立です。

1)デバイスのエンディアン(バイトオーダ)が、バイトが配列に格納されている順序と一致し、2)配列が整列されたメモリアドレスに配置されていることがわかっている場合、次のような解決策が可能です。後者の場合はマシンに依存しますが、16ビット配列を表すchar配列が偶数アドレスで開始し、32ビットの場合は4で割り切れるアドレスで開始する必要があります。この場合、いくつかのポインタトリックの後に、単にアドレスを読むことができます:

uint16_t res = *(uint16_t *)&char_array[xxx];

xxx はメモリの最初のバイトに対応する配列インデックスです。これは、最小値のインデックスと同じではない可能性があることに注意してください。

endianess-neutralなので、最初のクラスのソリューションを強くお勧めします。

とにかく、両方ともあなたの memcpy よりも速いです。

3
追加された
あなたがユニオンを使用して変換するのが上記のシフト方法より速いかどうか知っていますか?
追加された 著者 Muis,
これはアーキテクチャによって異なります。また、データを共用体にコピーする必要がある場合、またはchar配列を共用体として直接扱うことができるかどうかは異なります。理想的な場合、バイトが正しい順序で整列されている場合、1つの機械命令だけが必要です。そうでない場合は、2つ(または4つ)の文字を配列から読み込み、それらを共用体(通常はメモリーに格納されている)に保存してから全オブジェクトを読み取る必要があります。通常、これは2つ(または4つ)の読み取りよりもはるかに高価であり、プロセッサレジスタで実行できるシフトが続きます。アセンブラの出力を確認するか、実行してください。
追加された 著者 Lindydancer,

memcpy is not valid for "shifting" (moving data by an offset shorter than its length within the same array); attempting to use it for such invokes very dangerous undefined behavior. See http://lwn.net/Articles/414467/

memmove または自分のシフトループを使用する必要があります。約64バイトを超えるサイズの場合、私は memmove がもっと速くなることを期待しています。非常に短いシフトのために、あなた自身のループが勝つかもしれません。 memmove は、どの方向のコピーが安全であるかを判断する必要があるため、 memcpy よりもオーバーヘッドが大きいことに注意してください。あなた自身のループはどの方向が安全であるか(おそらく)知っているので、余分なランタイムチェックを避けることができます。

2
追加された
私はちょうど配列から単一のvariabeleに4バイトをコピーするので、データを移動しません。それはすべてです:memcpy(long、char_array [offset]、4)
追加された 著者 Muis,
OPは "ビットシフト"を意味し、メモリを動かさなかった。 :)
追加された 著者 Graham Borland,
OK私は誤解しました。サンプルが一列に並んでおり、エイリアスの規則に違反していないことを100%保証できる場合を除き、 memcpy を使用する必要があります。コンパイラは、可能であれば、単一のロード/ストア(関数呼び出しなし)にコンパイルします。
追加された 著者 R..,