[c] どのように標準ライブラリを使用してのみアライメントメモリを割り当てるには?



Answers

あなたが質問をどのように見ているかによって、3つの少し異なる答えがあります:

1)Jonathan Lefflerの解答は、16行に丸めて16バイトではなく、15バイトしか必要ないという点を除いて、正確な質問には十分です。

A:

/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;

B:

free(mem);

2)より一般的なメモリ割り当て関数の場合、呼び出し側は2つのポインタ(1つを使用し、1つを解放する)を追跡する必要はありません。 したがって、整列したバッファの下にある '実'バッファへのポインタを格納します。

A:

void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;

B:

if (ptr) free(((void**)ptr)[-1]);

memに15バイトしか追加されていない(1)と違って、このコードでは実装がmallocからの32バイト整列を保証する場合、実際には整列を減らすことができます(おそらく理論的にCの実装では32バイト)。 memset_16alignedを呼び出すだけであれば問題ありませんが、構造体にメモリを使用すると問題になる可能性があります。

実装固有の配置保証が何であるかをプログラマチックに判断する方法がないので、返されたバッファが必ずしも任意の構造体には適していないことをユーザーに警告する以外に、良い解決策が何であるかはわかりません。 私は起動時に2つ以上の1バイトバッファを割り当てることができ、最悪のアラインメントは保証されたアラインメントであると仮定します。 あなたが間違っているなら、あなたは記憶を浪費します。 より良いアイデアをお持ちの方、どうぞ...

[ 追加されました : '標準的な'トリックは、必要なアラインメントを決定するために、 '最大限整列したタイプである可能性が高い'という結合を作成することです。 最大で整列された型は(C99で) ' long long '、 ' long double '、 ' void * '、または ' void (*)(void) 'である可能性が高い。 <stdint.h>をインクルードすると、 long long代わりに ' intmax_t 'を使用する可能性があります(Power 6(AIX)マシンでは、 intmax_tは128ビットの整数型を与えます)。 その共用体の整列要件は、それを単一のchar型の構造体に組み込み、その後に共用体を入れることによって決定できます。

struct alignment
{
    char     c;
    union
    {
        intmax_t      imax;
        long double   ldbl;
        void         *vptr;
        void        (*fptr)(void);
    }        u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;

次に、要求されたアライメント(この例では16)と上記で計算されたalign値のうち大きい方を使用します。

(64ビット)Solaris 10では、 malloc()結果の基本的な配置は32バイトの倍数であるようです。
]

実際には、アライメントされたアロケータは、ハードワイヤードではなく、アラインメントのパラメータを取ることがよくあります。 したがって、ユーザーは気になる構造体のサイズ(またはそれよりも大きい2の最小パワー)を渡し、すべてがうまくいくでしょう。

3)あなたのプラットフォームが提供するものを使用してください: posix_memalign for POSIX、 _aligned_malloc (Windows)

4)C11を使用する場合、この最も簡潔でポータブルで簡潔なオプションは、このバージョンの言語仕様で導入された標準ライブラリ関数aligned_allocを使用することです。

Question

私はちょうど就職の面接の一環としてテストを終えました。そして、1つの質問が私を困らせました。 私はの乗組員が何をすることができるかを見たいと思います:

memset_16aligned関数は16byteのアライメントされたポインタを渡す必要があり、そうでないとクラッシュします。

a)1024バイトのメモリをどのように割り当て、それを16バイトの境界に揃えますか?
b)memset_16alignedが実行された後にメモリを解放する。

{

   void *mem;

   void *ptr;

   // answer a) here

   memset_16aligned(ptr, 0, 1024);

   // answer b) here

}



ここでは、「切り上げ」の部分に対する別のアプローチがあります。 最も鮮明にコード化されたソリューションではありませんが、それは仕事を完了させます。このタイプの構文は少し覚えやすいです(2の累乗ではないアライメント値でも機能します)。 uintptr_tキャストは、コンパイラーを和らげるために必要でした。 ポインタの算術演算は、除算や乗算があまり好きではありません。

void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);



いくつかの16バイトを追加し、元のptrを16ビットに合わせるには、ポインタの下に(16-mod)を追加します。

main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );

printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );


free(mem1);
}



For the solution i used a concept of padding which aligns the memory and do not waste the memory of a single byte .

If there are constraints that, you cannot waste a single byte. All pointers allocated with malloc are 16 bytes aligned.

C11 is supported, so you can just call aligned_malloc (16, size).

void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);






Accelerate.frameworkは常にベクトル化されたOS X / iOSライブラリで、常に整列に注意を払う必要があります。 非常にいくつかの選択肢がありますが、そのうちの1つまたは2つは上記のとおりです。

このような小さな配列の最速の方法は、スタックに貼り付けるだけです。 GCC / clangの場合:

 void my_func( void )
 {
     uint8_t array[1024] __attribute__ ((aligned(16)));
     ...
 }

無料()は必要ありません。 これは一般的に2つの命令です:スタックポインタから1024を引いた後、スタックポインタを-alignmentでANDします。 おそらく、配列の寿命がスタックを超過したか、再帰が働いているか、またはスタック領域が重大なプレミアムであるため、リクエスタはヒープ上のデータを必要としていました。

OS X / iOSでは、malloc / calloc / etcへのすべての呼び出し。 常に16バイトに整列します。 たとえば、AVX用に32バイトの配列が必要だった場合は、posix_memalignを使用できます。

void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
   RunInCirclesWaivingArmsWildly();
...
free(buf);

いくつかの人々は、同様に動作するC ++インタフェースについて言及しています。

ページが2の大きな累乗にアライメントされていることを忘れてはならないので、ページアライメントされたバッファも16バイトアライメントされます。 したがって、mmap()およびvalloc()および他の同様のインタフェースもオプションです。 mmap()には、必要に応じて、バッファに初期化されていないものを何らかの形で割り当てることができるという利点があります。 これらのページは一辺に整列しているので、これらから最小限の割り当てを得ることはできません。また、初めてVMに違反する可能性があります。

チーズ:ガードmallocなどをオンにします。 VMはオーバランをキャッチするために使用され、その境界はページの境界にあるため、このようなn * 16バイトのサイズのバッファはn * 16バイトになります。

一部のAccelerate.framework関数は、ユーザが指定した一時バッファを取り込んでスクラッチ領域として使用します。 ここでは、私たちに渡されたバッファが大きく整列していないと仮定しなければならず、ユーザーは積極的に私たちの人生を邪魔にならないようにしようとしています。 (ここでは、テンポラリバッファの前後にガードページを貼り付けて、下線を引いています)ここでは、16バイトの整列したセグメントをどこかに確保するために必要な最小サイズを返します。 このサイズはdesired_size + alignment - 1です。したがって、この場合は1024 + 16 - 1 = 1039バイトです。 次に、

#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
    uint8_t *alignedBuf = (uint8_t*) 
                          (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) 
                                        & -((uintptr_t) alignment));
    ...
}

アラインメント-1を追加すると、最初にアライメントされたアドレスを越えてポインタが移動し、-alignment(たとえば0xfff ... ff0でアライメント= 16)とANDされて、アライメントされたアドレスに戻ります。

他の投稿で説明されているように、16バイト境界整列が保証されていない他のオペレーティングシステムでは、より大きなサイズのmallocを呼び出し、free()の後ろにポインタを置いてすぐ上で説明したように整列し、一時バッファの場合について説明します。

aligned_memsetに関しては、これはむしろ愚かです。 アライメントされたアドレスに到達するまで最大15バイトまでループするだけで、最後にアライメントされたストアとそれに続くクリーンアップコードを実行する必要があります。 整列された領域をオーバーラップする非整列のストア(長さが少なくともベクトルの長さである場合)またはmovmaskdquのようなものを使用して、ベクトルコードでクリーンアップビットを実行することもできます。 誰かがちょうど怠け者になっています。 インタビュアーがあなたがstdint.h、ビット演算子、およびメモリの基礎に慣れているかどうかを知りたければ、合理的なインタビューの質問になるでしょう。




MacOS X固有:

  1. mallocで割り当てられたすべてのポインタは16バイトに整列しています。
  2. C11がサポートされているので、aligned_malloc(16、size)を呼び出すことができます。

  3. MacOS Xはmemset、memcpy、memmoveの起動時に個々のプロセッサーに最適化されたコードを選択します。 99%の確率でmemsetが手書きのmemset16より高速に実行され、問題全体が無意味になります。

あなたが100%ポータブルなソリューションを望むなら、C11の前には何もありません。 ポインタのアライメントをテストする移植可能な方法がないためです。 100%ポータブルでなければならない場合は、

char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;

これは、ポインタをunsigned intに変換するときに、ポインタのアラインメントが最下位ビットに格納されていることを前提としています。 unsigned intに変換すると情報が失われ、実装が定義されますが、結果をポインタに戻さないため問題はありません。

恐ろしい部分はもちろん、元のポインタはfree()を呼び出すためにどこかに保存しなければなりません。 だから私は本当にこのデザインの知恵を疑うでしょう。




16対15バイトカウントのパディングフロントでは、Nのアライメントを得るために追加する必要がある実際の数はmax(0、NM)です。ここで、Mはメモリアロケータの自然なアラインメントです(両方とも2の累乗です)。

任意のアロケータの最小メモリアライメントは1バイトなので、15 = max(0,16-1)は控えめな答えです。 しかし、メモリアロケータが32ビットのint整列アドレス(これはかなり一般的です)を与えることが分かっている場合は、12をパッドとして使用することができます。

これはこの例では重要ではありませんが、12KのRAMを搭載した組み込みシステムでは、すべての単一のintがカウントされることが重要です。

あなたが実際にすべてのバイトを保存しようとしている場合、それを実装する最良の方法は、マクロとしてあなたのネイティブメモリアライメントを供給することができます。 繰り返しますが、おそらく、すべてのバイトを保存する必要がある組み込みシステムでのみ有効です。

以下の例では、ほとんどのシステムで、値1はMEMORY_ALLOCATOR_NATIVE_ALIGNMENTにはMEMORY_ALLOCATOR_NATIVE_ALIGNMENTありませんが、理論上の32ビットアラインメントの組み込みシステムでは、以下のように貴重なメモリを節約できます。

#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT    4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)





Related