web-dev-qa-db-ja.com

mallocはアライメントをどのように理解しますか?

here からの抜粋

pw = (widget *)malloc(sizeof(widget));

生のストレージを割り当てます。確かに、malloc呼び出しは、十分な大きさのストレージを割り当てますウィジェットを保持するために適切に位置合わせ

fast pImpl も参照してください。

位置合わせ。任意のメモリアライメント。動的に割り当てられるメモリnewまたはmallocを介して適切に整列されることが保証されます任意のタイプのオブジェクトに対して、動的に割り当てられないバッファにはそのような保証はありません

私はこれに興味がありますが、mallocはカスタムタイプのアライメントをどのように知っていますか?

47
Chang

位置合わせの要件は再帰的です:structの位置合わせは、そのメンバーのいずれかの最大の位置合わせであり、これは再帰的に理解されます。

たとえば、各基本型のアライメントがそのサイズに等しいと仮定すると(これは常に一般的に正しいとは限りません)、_struct X { int; char; double; }_のアライメントはdoubleであり、倍数になるようにパディングされますdoubleのサイズ(4(int)、1(char)、3(padding)、8(double))。 _struct Y { int; X; float; }_にはXのアライメントがあり、これはdoubleのアライメントと最大で等しく、Yはそれに応じてレイアウトされます:4(int) 、4(パディング)、16(X)、4(フロート)、4(パディング)。

(すべての数値は単なる例であり、マシンによって異なる場合があります。)

したがって、基本的なタイプに分類することで、ほんの一握りの基本的なアライメントを知るだけでよく、その中にはよく知られている最大のアライメントがあります。 C++は、その整列isその最大の整列であるタイプ_max_align_t_も定義します。

malloc()が行う必要があるのは、その値の倍数であるアドレスを選択することだけです。

47
Kerrek SB

ハーブサッターの引用の中で最も関連性の高い部分は、太字でマークした部分だと思います。

アライメント。任意のメモリアライメント。 newまたはmallocを介して動的に割り当てられたメモリは、オブジェクト任意のタイプに対して適切にアライメントされることが保証されていますが、動的に割り当てられていないバッファにはそのような保証がありません

anyタイプに合わせて調整するため、どのタイプを念頭に置いているかを知る必要はありません。どのシステムでも、必要または意味のある最大アライメントサイズがあります。たとえば、4バイトのワードを使用するシステムでは、最大で4バイトのアライメントが可能です。

これは malloc(3) man-page によっても明確になります。

malloc()およびcalloc()関数は、適切に位置合わせされた割り当て済みメモリへのポインタを返しますあらゆる種類の変数に対して

21
ruakh

malloc()が使用できる唯一の情報は、それに渡されるリクエストのサイズです。一般に、渡されたサイズを最も近い(または等しい)2のべき乗に切り上げ、その値に基づいてメモリを調整するようなことをします。 8バイトなど、アライメント値の上限もある可能性があります。

上記は仮想的な議論であり、実際の実装は、使用しているマシンアーキテクチャとランタイムライブラリによって異なります。おそらく、あなたのmalloc()は常に8バイトにアラインされたブロックを返し、何も違うことをする必要はありません。

5
Greg Hewgill

1)すべての配置の最小公倍数に配置します。例えばintが4バイトのアライメントを必要とするが、ポインターが8バイトを必要とする場合、すべてを8バイトのアライメントに割り当てます。これにより、すべてが整列されます。

2)サイズ引数を使用して、正しい配置を決定します。小さいサイズの場合、malloc(1)(他のタイプのサイズが1ではないと仮定)などのタイプは常にcharです。 C++ newにはタイプセーフであるという利点があるため、常にこの方法でアライメントを決定できます。

3
Pubby

C++ 11より前のアライメントは、正確な値が不明であり、malloc/callocが引き続きこのように機能する最大のアライメントを使用することにより、かなり単純に扱われていました。これは、malloc割り当てがすべてのタイプに対して正しく調整されることを意味します。

間違ったアライメントは、標準に従って未定義の動作を引き起こす可能性がありますが、x86コンパイラは寛大で、パフォーマンスが低下するだけで罰することがあります。

コンパイラオプションまたはディレクティブを使用してアライメントを調整することもできます。 (たとえばVisualStudioのプラグマパック)。

しかしplacement newとなると、C++ 11はalignofおよびalignas。と呼ばれる新しいキーワードをもたらします。コンパイラの最大アライメントが1より大きい場合の効果。最初のplacement newは自動的に適切ですが、2番目は適切ではありません。

#include <iostream>
#include <malloc.h>
using namespace std;
int main()
{
        struct A { char c; };
        struct B { int i; char c; };

        unsigned char * buffer = (unsigned char *)malloc(1000000);
        long mp = (long)buffer;

        // First placment new
        long alignofA = alignof(A) - 1;
        cout << "alignment of A: " << std::hex << (alignofA + 1) << endl;
        cout << "placement address before alignment: " << std::hex << mp << endl;
        if (mp&alignofA)
        {
            mp |= alignofA;
            ++mp;
        }
        cout << "placement address after alignment : " << std::hex <<mp << endl;
        A * a = new((unsigned char *)mp)A;
        mp += sizeof(A);

        // Second placment new
        long alignofB = alignof(B) - 1;
        cout << "alignment of B: " <<  std::hex << (alignofB + 1) << endl;
        cout << "placement address before alignment: " << std::hex << mp << endl;
        if (mp&alignofB)
        {
            mp |= alignofB;
            ++mp;
        }
        cout << "placement address after alignment : " << std::hex << mp << endl;
        B * b = new((unsigned char *)mp)B;
        mp += sizeof(B);
}

このコードのパフォーマンスは、ビット単位の操作で改善できると思います。

編集:高価なモジュロ計算をビット演算に置き換えました。誰かがもっと早く何かを見つけることをまだ望んでいます。

2

mallocは、パラメーターが単なる合計サイズであるため、何を割り当てているのかを認識しません。任意のオブジェクトに対して安全な配置に配置するだけです。

0
John Paul