私は、MPI forハイパフォーマンスコンピューティング(10 ^ 5-10 ^ 6コア)を使用するプロジェクトのために、他の誰かのC++コードをレビューしています。このコードは、(潜在的に)異なるアーキテクチャーの異なるマシン。彼は次のように何かを言うコメントを書きました:
通常は
new
とdelete
を使用しますが、ここではmalloc
とfree
を使用しています。一部のコンパイラはnew
を使用するとデータに異なる方法でパディングし、異なるプラットフォーム間でのデータ転送でエラーが発生するため、これが必要です。これはmalloc
では起こりません。
これは、標準のnew
対malloc
の質問から私が知っていることには適合しません。
new/deleteとmalloc/freeの違いは何ですか? コンパイラーがオブジェクトのサイズを異なる方法で計算できるという考えを示唆しています(しかし、なぜsizeof
の使用とは異なるのですか? ?)。
malloc&配置new vs. new はかなり人気のある質問ですが、new
がコンストラクタを使用しないmalloc
についてのみ話しますが、これは関係ありません。
mallocはアラインメントをどのように理解しますか? は、メモリがnew
またはmalloc
と正しくアラインメントされることが保証されていると述べています。
私の推測では、彼は過去に自分のバグを誤って診断し、new
とmalloc
は異なる量のパディングを与えると推測しましたが、これはおそらく本当ではないと思います。しかし、Googleや以前の質問で答えを見つけることができません。
StackOverflow、私を助けてください、あなたは私の唯一の希望です!
IIRCには、うるさい点が1つあります。 malloc
は、標準の型に合わせて配置されたアドレスを返すことが保証されています。 ::operator new(n)
は、標準の型に合わせて配置されたアドレスを返すことが保証されていますn以下、およびT
が文字型でない場合は_new T[n]
_は、T
に合わせて整列されたアドレスを返す場合にのみ必要です。
ただし、これは、フラグを格納するためにポインターの下位数ビットを使用するなど、実装固有のトリックを実行している場合や、厳密に必要とするよりも多くのアライメントをアドレスに依存している場合にのみ関連します。
オブジェクト内のパディングには影響しません。オブジェクトが占有するメモリをどのように割り当てたかに関係なく、レイアウトは必ず同じレイアウトになります。そのため、違いがデータ転送のエラーにつながる可能性があることを確認するのは困難です。
彼の意見では、それらが「mallocのようにパディングされている」か「新しいようにパディングされている」かに関係なく、そのコメントの作成者がスタックまたはグローバル内のオブジェクトについてどのような兆候を持っていますか?それはアイデアがどこから来たかの手掛かりを与えるかもしれません。
彼は混乱しているかもしれませんが、彼が話しているコードは、malloc(sizeof(Foo) * n)
と_new Foo[n]
_の単なる違いではありません。多分それはもっと似ています:
_malloc((sizeof(int) + sizeof(char)) * n);
_
vs.
_struct Foo { int a; char b; }
new Foo[n];
_
つまり、多分彼は言って "mallocを使用する"かもしれませんが、means "構造体を使用する代わりに手動でデータを整列されていない場所にパックします"。実際には、構造体を手動でパックするためにmalloc
は必要ありませんが、それが混乱の度合いが少ないことに気付かない場合。ネットワーク経由で送信されるデータレイアウトを定義する必要があります。 structが使用されている場合、実装が異なるとデータの埋め込み方法も異なります。
あなたの同僚は、new[]/delete[]
のマジッククッキーを念頭に置いていた可能性があります(これは、実装が配列を削除するときに使用する情報です)。ただし、new[]
によって返されたアドレスから始まる割り当てが使用されている場合(アロケーターの割り当てではなく)、これは問題ではありませんでした。
Packingのほうがありそうです。 ABIのバリエーションにより、(たとえば)構造体の最後に異なる数の後続バイトが追加される可能性があります(これはアライメントの影響を受けます。配列も考慮してください)。 mallocを使用すると、構造体の位置を指定できるため、外部のABIに簡単に移植できます。これらの変動は通常、転送構造の配置とパッキングを指定することによって防止されます。
私はあなたが正しいと思います。パディングはnew
またはmalloc
ではなく、コンパイラによって行われます。 new
またはmalloc
をまったく使用せずに配列または構造体を宣言した場合でも、パディングに関する考慮事項が適用されます。いずれにせよ、プラットフォーム間でコードを移植するときにnew
とmalloc
の異なる実装がどのように問題を引き起こす可能性があるかはわかりますが、プラットフォーム間でデータを転送する際に問題が発生する方法はまったくわかりません。
オブジェクトのレイアウトは、malloc
またはnew
のどちらを使用して割り当てられたかに依存できません。どちらも同じ種類のポインタを返します。このポインタを他の関数に渡すと、オブジェクトがどのように割り当てられたかがわかりません。 sizeof *ptr
は、それがどのように割り当てられたかではなく、ptr
の宣言に依存します。
これは、これがどこから来ているのか、私の推測です。おっしゃったように、問題はMPIを介したデータ転送にあります。
個人的には、MPIを介して送受信したい複雑なデータ構造のために、charの配列に全体をパックまたはアンパックするシリアル化/デシリアライズメソッドを常に実装しています。これで、パディングにより、構造のサイズがそのメンバーのサイズよりも大きくなる可能性があることがわかったため、データ構造のパディングされていないサイズを計算して、送受信されるバイト数を知る必要があります。
たとえば、上記の方法で_std::vector<Foo> A
_を送受信したい場合、MPIを使用して、生成される文字配列のサイズをA.size()*sizeof(Foo)
であると想定するのは誤りです。つまり、serialize/deserializeメソッドを実装する各クラスは、配列のサイズを報告するメソッドも実装する必要があります(または、配列をコンテナーに保存するほうがよいでしょう)。これは mayがバグの背後にある理由になりますが、このスレッドで指摘されているように、何らかの方法でnew
とmalloc
は関係ありません。
c ++では:new
keywordは、データ構造に関して特定のメモリバイトを割り当てるために使用されます。たとえば、いくつかのクラスまたは構造を定義し、そのオブジェクトにメモリを割り当てたいとします。
myclass *my = new myclass();
または
int *i = new int(2);
しかし、すべての場合において、定義されたデータ型(クラス、構造体、共用体、整数、文字など...)が必要であり、そのオブジェクト/変数に必要なメモリのバイトのみが割り当てられます。 (つまり、そのデータ型の倍数)。
ただし、malloc()メソッドの場合、メモリの任意のバイトを割り当てることができ、常にデータ型を指定する必要はありません。ここでは、malloc()のいくつかの可能性でそれを観察できます。
void *v = malloc(23);
または
void *x = malloc(sizeof(int) * 23);
または
char *c = (char*)malloc(sizeof(char)*35);
プレーンな古いデータ構造のレイアウトを制御する場合、MS Visualコンパイラーでは#pragma pack(1)
を使用します。そのようなプリコンパイラ指令は、たとえば gcc のように、ほとんどのコンパイラでサポートされていると思います。
これは、構造のすべてのフィールドを空のスペースなしで前後に配置する結果になります。
反対側のプラットフォームが同じことを行う場合(つまり、データ交換構造を1のパディングでコンパイルした場合)、両側で取得されたデータは適切に適合します。したがって、C++でmallocを使用する必要がありませんでした。
最悪の場合、C++でmallocを直接使用するのではなく、新しい演算子をオーバーロードして、トリッキーな処理を実行することを検討しました。