2011-06-19 15 views
3

malloc()/ free()の使用方法をSTLライクなアロケータの周りのラッパーに置き換える移植可能な方法はありますか?malloc()/ free()の代わりにアロケータを使用していますか?

コンテキスト:メモリ管理のためのカスタムmalloc()/ free()のような関数を指定することができ、マルチスレッドのコンテキストで使用されるCライブラリがあります。 GCC-libstdC++のmt_allocは、優れたマルチスレッドアロケータを探し求めています。今私は、Cのライブラリでそれを使用したいと思いますが、それを行う方法?

私が見る主な問題は、deallocate()関数のfree()とは異なり、アドレスに加えて割り当てられたメモリブロックのサイズを取ることです。ですから、メモリを解放するときにdeallocate()にフィードバックすることができるように、すべてのメモリ割り当てに関連するサイズを追跡する必要があります。私がこれを解決するために考えていた最も簡単な解決策は、メモリブロックの先頭に割り当てられたメモリのサイズを格納することですが、次に発生する可能性のある配置の問題を解決する方法がわかりません。

私が見落としている簡単な解決策はありますか?

+0

コンテナは、ますます大きなチャンクにメモリを割り当て、サイズが小さくなったときのメモリはすべて保持します。あなたのCライブラリはおそらく同じ使用パターンを持っていないでしょうから、コンテナと同じパフォーマンスの向上さえ見られないかもしれません。 –

+0

@Emile:サイズを把握するために考えていたのは、チャンクのサイズを格納するために余分なスペースを割り当てることでした。したがって、nバイトが要求された場合は、n + sizeof(std :: size_t)(+配列の考慮事項)のようなものを割り当て、ベースアドレス+ sizeof(std :: size_t)を返します。ポインタpを解放するときは、p - sizeof(std :: size_t)をとり、サイズを読み込んでdeallocate()に渡します。 – bluescarni

+0

ええ、私はあなたの質問を読んだときに何とかそれを逃しました。 ADDである必要があります。 :-) –

答えて

3

私のプラットフォームでは、mallocは割り当てられたメモリが8バイトの境界に配置されることを保証します。ご使用のプラットフォーム、適切な型に置き換えてuint64_tについて

#include <stdint.h> 
#include <ext/mt_allocator.h> 

static __gnu_cxx::__mt_alloc<uint64_t> theAllocator; 

void* mtmalloc(size_t size) 
{ 
    // Divide size by sizeof(uint64_t) and round up 
    size_t payloadElementCount = (size + sizeof(uint64_t) - 1)/
           sizeof(uint64_t); 

    // Add an extra uint64_t to store the chunk size 
    size_t chunkElementCount = 1 + payloadElementCount; 

    // Allocate the chunk 
    uint64_t* chunk = theAllocator.allocate(chunkElementCount); 

    // Store the chunk size in the first word 
    chunk[0] = chunkElementCount; 

    // Return a pointer past where the chunk size is stored 
    return static_cast<void*>(chunk + 1); 
} 

void mtfree(void* pointer) 
{ 
    // The chunk begins one word before the passed in pointer 
    uint64_t* chunk = static_cast<uint64_t*>(pointer) - 1; 

    // Retrieve the chunk size 
    size_t chunkElementCount = chunk[0]; 

    // Deallocate the chunk 
    theAllocator.deallocate(chunk, chunkElementCount); 
} 

int main() 
{ 
    int* array = (int*)mtmalloc(sizeof(int) * 4); 
    array[0] = 0; 
    array[1] = 1; 
    array[2] = 2; 
    array[3] = 3; 
    mtfree(array); 
} 

:この動作を模倣するために、allocator<uint64_t>を使用しています。

これはValgrindのようなものでテストして、メモリリークがないことを確認する必要があります。


代わりのuint64_t、あなたはGCCコンパイラへのソリューションポータブル用GCCの__BIGGEST_ALIGNMENT__とブーストのaligned_storagetype traitを使用することができます。

typedef boost::aligned_storage<__BIGGEST_ALIGNMENT__, __BIGGEST_ALIGNMENT__> AlignedType;

+0

size_t chunkSize = 1 + payloadSize; uint64_t *へのポインタキャストを行いながら、サイズを1バイト増加させます。その後、実際にポインタsizeof(uint64_t)を1減らします。基本的には、誰かがXバイトを割り当てようとしたときに、実際にX-(sizeof(uint64_t)-1)バイトを割り当ててそのようなポインタを返すことを意味しますか? – Simon

+1

@imim: 'allocator :: allocate'は、バイト数ではなく**要素**をパラメータとして取ります。 http://cplusplus.com/reference/std/memory/allocator/allocate/ –

+0

を参照してくださいAhh私は:)私の悪い、+1! – Simon

0

参照my answer hereブロックの先頭に値を格納に関する。必要に応じてわずかに変更することができます。

0

私が知っているオブジェクトサイズトラッキングの主な2つの方法は、側面にあるメタデータを持つサイズ分離アロケータ(Kingsleyスタイルのアロケータなど)に暗黙的に含まれているか、オブジェクトの前にサイズを固定することですオブジェクトヘッダー(例:dlmalloc)。かなりひどい第三の解決策は、割り当てられたすべてのオブジェクトとそのサイズのマップを維持することです。もちろん、そのマップは別のアロケータによって管理されます。

私はあなたが正しい軌道に乗っていると思います。あなたが整列の考慮事項を認識していることは良いことです。私は、mt_allocに関する情報を参照して、選択肢や驚きがあるかどうかを調べようとしましたが、そのような情報はすぐには出てこないようです。いくつかのアロケータには、オブジェクトサイズを問い合わせるメソッドがあります(これは安価かもしれません)。 deallocate関数がサイズを明示的に渡す必要がある場合、そのような関数は存在しないと推測しますが、あなたは決して知りません。

アラインメントが重要な場合、アロケータはおそらくメモリが適切に整列して返されないため、計算を微調整する必要があります。あなたが返されるポインタのアライメントについて何も知らない場合は、あなたが何か必要があります。mt_allocは内部ポインタ上のオブジェクトを解放耐えることができない場合は

struct object_header { 
    size_t size; 
}; 

void * buf = xxmalloc (2 * alignment + size + sizeof(object_header)); 
void * alignedPtr = (void *) (((size_t) buf + sizeof(object_header) + alignment - 1) & ~(alignment - 1)); 

を、このスキームは、あるため、アライメントのための余分なスペースをパディングすることにより、あなたのための問題を提起します返された元の住所はもはやわかりません。その場合は、ヘッダーに余分なフィールドを格納する必要があります。

mt_allocがメモリを内部的に管理する方法によっては、余分なヘッダーをタックすると相当なオーバーヘッドが発生する可能性があります。サイズを分離したアロケータでは、このヘッダーをタックすることで、最大でページサイズまでのオブジェクトに対して2倍のスペースオーバーヘッドを与えることができます。このとき、オブジェクトごとに余分なページのコストを支払うことができます。他のアロケータでは、これは問題ではないかもしれませんが、それは目を引くものです。

+0

mallocのアラインメントはいつから8バイトですか?"割り当てが成功した場合に返されるポインタは、 がオブジェクトの任意のタイプ へのポインタに割り当てられるように適切に整列され、そのオブジェクトまたは配列にアクセスするために使用されます。 (スペースが が明示的に割り当て解除されるまで)。 "、明らかにプラットフォーム(およびコンパイラ)固有のものです。 – Simon

+0

あなたはそうです、私はあまりにも長い間GNUの土地で働いています。実際には、私たちが割り振り者の周りにこれを強制するためにレイヤーをラップしていたので、私はよく知っておくべきです。 –

1

Paul Laskaがaltdevblogadayに書いた素晴らしいシリーズがあります。最初の記事へのリンクは次のとおりです。http://altdevblogaday.org/2011/04/11/ready-set-allocate-part-1/

この記事では、彼はブロックサイズ割り当てとアライメントの問題に注意しています。割り当て解除の問題を処理するための、よく考えられた、よく書かれたソリューションを提供する必要があります。

関連する問題