mallocとnew —異なるパディング


110

高性能コンピューティング(10 ^ 5-10 ^ 6コア)にMPIを使用するプロジェクトのために、他の誰かのC ++コードをレビューしています。コードは、異なるアーキテクチャ上の(潜在的に)異なるマシン間の通信を可能にすることを目的としています。彼は次のようにコメントを書いています。

通常はnewandを使用しますdeleteが、ここではmallocand を使用していfreeます。これが必要なのは、一部のコンパイラは、new使用時にデータに異なるパディングを行うため、異なるプラットフォーム間でのデータ転送でエラーが発生するためです。これはで発生しませんmalloc

これは、私が標準newmalloc質問から知っている何にも適合しません。

new / deleteとmalloc / freeの違いは何ですか?コンパイラがオブジェクトのサイズを異なる方法で計算する可能性があるという考えをほのめかします(しかし、なぜそれが使用と異なるのsizeofですか?)

mallocと配置new vs. newはかなり人気のある質問ですがnewmallocそうでない場合のコンストラクタの使用についてのみ話しますが、これは関係ありません。

mallocはどのようにアラインメントを理解しますか?メモリはどちらかと適切に整列することが保証されているか、newまたはmalloc私が以前考えていたものであると述べています。

私の推測では、彼は過去に自分のバグを誤って診断し、それを推定しnewmalloc、異なる量のパディングを提供していると思いますが、これはおそらく正しくないと思います。しかし、Googleや以前の質問で答えを見つけることができません。

StackOverflow、私を助けてください、あなたは私の唯一の希望です!


33
さまざまなSOスレッドの研究だけで+1!
iammilind

7
+1私が長い間SOで見た中で、「私が他人に尋ねる前に自分を助ける」研究の仕事の1つであるのは簡単です。これをもう数回賛成できるといいのですが。
WhozCraig

1
転送コードは、データが特定の方法で整列されていることを前提としていますか?たとえば、8バイト境界で開始するとしますか?これは、間で異なる可能性mallocnew、などnew一部の環境でブロックを割り当て、先頭にいくつかのデータを追加し、右このデータの後の場所へのポインタを返します。(私は、データブロック内で、他の人と同意、mallocおよびnewパディングの同じ種類を使用する必要があります。)
Lindydancer

1
わあ、この質問がこれほど人気が​​あるとは思っていませんでした。@ Lindydancer、8バイト境界が想定されているとは思いません。興味深い点ですが。
hcarver

1
ある割り当て方法を別の割り当て方法で使用する1つの理由は、「他の誰か」がオブジェクトの解放を行っているときです。この「他の誰か」がfreeを使用してオブジェクトを削除する場合は、mallocを使用して割り当てる必要があります。(パッドの問題は赤いニシンです。)
Lindydancer

回答:


25

IIRCには、うるさい点が1つあります。malloc標準タイプに合わせて配置されたアドレスを返すことが保証されています。nを超えない::operator new(n)標準の型に対して整列されたアドレスを返すことが保証されているだけであり、が文字型でない場合は、に対して整列されたアドレスを返すことのみが必要です。Tnew T[n]T

ただし、これは、フラグを格納するためにポインタの最下位の数ビットを使用するなど、実装固有のトリックを実行している場合や、厳密に必要とするよりも多くのアラインメントをアドレスに依存している場合にのみ関連します。

オブジェクト内のパディングには影響しません。オブジェクトが占有するメモリをどのように割り当てたかに関係なく、レイアウトは必ず同じレイアウトになります。そのため、違いがデータ転送のエラーにつながる可能性があることを確認するのは困難です。

彼の意見では、それらが「mallocのようにパディングされている」か「新しいようにパディングされている」かに関係なく、そのコメントの作成者がスタックまたはグローバル内のオブジェクトについてどのような兆候を持っていますか?それはアイデアがどこから来たかの手掛かりを与えるかもしれません。

多分彼は混乱しているかもしれませんが、多分彼が話しているコードはmalloc(sizeof(Foo) * n)vsの単なる違いではありませんnew Foo[n]。多分それはもっと似ています:

malloc((sizeof(int) + sizeof(char)) * n);

struct Foo { int a; char b; }
new Foo[n];

つまり、おそらく「私はmallocを使用します」と言っているかもしれませんが、「構造体を使用する代わりに、データを整列されていない場所に手動でパックするという意味です。malloc構造体を手動でパックするために実際には必要ありませんが、それが混乱の度合いが少ないことに気付かない場合。ネットワーク経由で送信されるデータレイアウトを定義する必要があります。構造体が使用されている場合、実装が異なるとデータの埋め込み方法も異なります。


配置についてのポイントをありがとう。問題のデータはchar配列なので、私はそれがここの整列のものでも構造体のものでもないのではないかと思います-それも私の最初の考えでした。
hcarver

5
@Hbcdev:char配列はパディングされないので、説明は「混乱」します。
スティーブジェソップ

5

あなたの同僚はのnew[]/delete[]cookieを念頭に置いている可能性があります(これは、実装が配列を削除するときに使用する情報です)。ただし、new[](アロケータではなく)によって返されたアドレスから始まる割り当てが使用されている場合、これは問題ではありませんでした。

パッキングはより可能性が高いようです。ABIのバリエーションにより、(たとえば)構造体の最後に異なる数の後続バイトが追加される可能性があります(これはアライメントの影響を受けます。配列も考慮してください)。mallocを使用すると、構造体の位置を指定できるため、外部のABIに簡単に移植できます。これらの変動は通常、転送構造の配置とパッキングを指定することによって防止されます。


2
これが私が最初に考えたのは、「構造体はその部分の合計よりも大きい」という問題です。おそらく、これが彼のアイデアが最初に生まれた場所です。
hcarver

3

オブジェクトのレイアウトは、mallocまたはを使用して割り当てられたかどうかに依存できませんnew。どちらも同じ種類のポインタを返します。このポインタを他の関数に渡すと、オブジェクトがどのように割り当てられたかがわかりません。sizeof *ptrptr、それがどのように割り当てられたかではなく、の宣言に依存します。


3

私はあなたが正しいと思います。パディングは、コンパイラではなく、newまたはで行われmallocます。使用せずに、newまたはmallocまったく使用せずに配列または構造体を宣言した場合でも、パディングに関する考慮事項が適用されます。いずれにしても、プラットフォーム間でコードを移植するときにnew、さまざまな実装mallocが原因で問題が発生する可能性があることは確認できますが、プラットフォーム間でのデータ転送に問題が発生する可能性があることはまったくわかりません。


私は以前あなたが考える可能性が想定さしたいnewのための素晴らしいラッパーとしてmallocしかし、そうではありません他の回答から思わかなり真。コンセンサスは、パディングはどちらでも同じである必要があるということです。プラットフォーム間のデータ転送に関する問題は、転送メカニズムに欠陥がある場合にのみ発生すると思います:)
hcarver

0

プレーンな古いデータ構造のレイアウトを制御したい場合は、MS Visualコンパイラーを使用します#pragma pack(1)。このようなプリコンパイラディレクティブは、gccなどのほとんどのコンパイラでサポートされていると思います。

これは、構造のすべてのフィールドを空のスペースなしで前後に配置する結果になります。

反対側のプラットフォームが同じことを行う場合(つまり、データ交換構造を1のパディングでコンパイルした場合)、両側で取得されたデータは適切に適合します。したがって、C ++でmallocを使用する必要はありませんでした。

最悪の場合、C ++でmallocを直接使用するのではなく、新しい演算子をオーバーロードして、トリッキーな処理を実行することを検討しました。


データ構造のレイアウトを制御する必要があるのはどのような状況ですか?ちょっと興味があるんだけど。
hcarver

そして、誰かがコンパイラをサポートしているpragma packまたは知っていることを知っていますか?私はそれが標準の一部ではないことを理解しています。
hcarver

たとえば、gccはそれをサポートしています。どのような状況で私はそれを必要としましたか:2つの異なるプレートフォーム間でバイナリデータを共有する:ウィンドウとPalmOS間、ウィンドウとLinux間でバイナリストリームを共有します。gccに関するリンク:gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Stephane Rolland

0

これは、これがどこから来ているのか、私の推測です。あなたが述べたように、問題はMPIを介したデータ送信にあります。

個人的には、MPIを介して送受信したい複雑なデータ構造に対して、charの配列との間で全体をパックまたはアンパックするシリアル化/逆シリアル化メソッドを常に実装しています。これで、パディングにより、構造体のサイズがそのメンバーのサイズよりも大きくなる可能性があることがわかったため、データ構造体のパディングされていないサイズを計算して、送受信されるバイト数を知る必要があります。

たとえばstd::vector<Foo> A、前述の手法を使用してMPI経由で送受信する場合、結果の文字配列のサイズがA.size()*sizeof(Foo)一般的であると想定するのは誤りです。言い換えると、serialize / deserializeメソッドを実装する各クラスは、配列のサイズを報告するメソッドを実装する必要があります(または、コンテナーに配列を格納する方がよいでしょう)。これバグの原因になるかもしれません。ただし、このスレッドで指摘されているように、newvs とは何の関係もありませんmalloc


char配列へのコピーは問題が発生する可能性があります-一部のコアがリトルエンディアンアーキテクチャ上にあり、一部のビッグエンディアンにある可能性があります(可能性は低いですが、可能です)。それらをXDRエンコードする必要がありますが、ユーザー定義のMPIデータ型を使用することもできます。彼らは簡単にパディングを考慮に入れます。しかし、私は誤解の考えられる原因についてあなたが言っていることを見ることができます-それは私が「構造体はその部分の合計よりも大きい」問題と呼んでいるものです。
hcarver '13 / 11/12

はい、MPIデータ型を定義することは、これを行う別の/正しい方法です。エンディアンについての良い点。とはいえ、実際のクラスターでそれが起こるかどうかは本当に疑わしいです。とにかく、彼らが同じ戦略に従うなら、これはバグにつながるかもしれないと思った...
mmirzadeh

0

c ++では: newキーワードを使用して、一部のデータ構造に対して特定のバイトのメモリを割り当てます。たとえば、いくつかのクラスまたは構造を定義し、そのオブジェクトにメモリを割り当てたいとします。

myclass *my = new myclass();

または

int *i = new int(2);

ただし、すべての場合で、定義されたデータ型(クラス、構造体、共用体、int、charなど...)が必要であり、そのオブジェクト/変数に必要なメモリのバイトのみが割り当てられます。(つまり、そのデータ型の倍数)。

ただし、malloc()メソッドの場合は、任意のバイトのメモリを割り当てることができ、常にデータ型を指定する必要はありません。ここでは、malloc()のいくつかの可能性でそれを観察できます。

void *v = malloc(23);

または

void *x = malloc(sizeof(int) * 23);

または

char *c = (char*)malloc(sizeof(char)*35);

-1

mallocは関数のタイプであり、newはc ++のc ++のデータ型のタイプです。必要以上にmallocを使用し、typecastを使用する必要がある場合は、コンパイラーによってエラーが発生し、メモリーの割り当てに不要な新しいデータタイプを使用した場合タイプキャストする


1
もう少し議論してみるべきだと思います。
カルロ

これは彼らがパディングで異なることをするという問題に対処していないようです、それは私が上で本当に尋ねていたものです。
hcarver
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.