1

パフォーマンスの前に人々は私の頭を引き裂きます:はい、私はこれを尋ねる前にプロファイリングを行いました:)

私はもう一度タイプコンテナの1つを調べていますが、機能するソリューションはありますが、キャッシュされるアイテムのタイプごとにヒープに個別に割り当てられるため、パフォーマンスが低下します(もちろん高価です)。

プログラムの入力の静的分析に基づいて、渡されるキャッシュオブジェクトに配置される可能性のあるすべてのオブジェクトに必要な合計サイズを知る方法を見つけました。基本的に、特定のキャッシュオブジェクトで構築できるオブジェクトのリストがあるので、キャッシュする必要がある可能性のあるサイズは事前にわかっていますが、コンパイル時ではなく、実行時のみです。

基本的に、私がやりたいのは、1つのメモリブロックを取得し、同じメモリブロック内にビットと制御対象オブジェクトをboost::make_shared構築することです。shared_ptr

キャッシュオブジェクトはコピー不可能であり、クライアントによってポインタによって渡されるため、コピー動作を維持することを心配する必要はありません(通常は、ptr_vectorまたはのようなものに格納されますstd::auto_ptr)。

ただし、そのようなコンテナをどのように正確に実装するか、つまり、配置制限などに従う方法についてはよくわかりません。

擬似コードで、私がやりたいこと:

//I know a lot of what's in here is not portable -- I need to run only on x86
//and x64 machines. Yes, this couple of classes looks hacky, but I'd rather
//have one hacky class than a whole programfull :)

class CacheRegistrar
{
    //Blah blah
public:
    //Figures out what objects will be in the cache, etc
    const std::vector<std::size_t>& GetRequiredObjectSizes() const;
    //Other stuff...
    template <typename T>
    void RegisterCacheObject();
    template <typename T>
    std::size_t GetObjectIndex() const;
    // etc.
};

class CacheObject;

std::auto_ptr<CacheObject> CacheObjectFactory(const CacheRegistrar& registrar)
{
    //Pretend this is in a CPP file and therefore CacheObject is defined...
    const std::vector<size_t>& sizes(registrar.GetRequiredObjectSizes());
    std::size_t sumOfCache = std::accumulate(sizes.begin(), sizes.end());
    sumOfCache += sizeof(CacheObject);
    boost::scoped_array<char> buffer(new char[] sumOfCache);
    CacheObject *obj = new (reinterpret_cast<void *>(buffer.get())) CacheObject;
    buffer.release(); //PSEUDOCODE (boost::scoped_array has no release member);
    return std::auto_ptr<CacheObject>(obj); //Nothrow
}

class CacheObject
{
    CacheRegistrar *registrar; //Set by my constructor
public:
    template<typename T>
    T& Get()
    {
        char * startOfCache = reinterpret_cast<char *>(this) + 
            sizeof(CacheObject);
        char * cacheItem = startOfCache + registrar->GetObjectIndex<T>();
        return *reinterpret_cast<T*>(cacheItem);
    }
};

私の一般的なコンセプトはここに聞こえますか?これを達成するためのより良い方法はありますか?

4

3 に答える 3

3

しかし、最初に、Andrei Alexandrescu によるこの記事を読んでください。その章で彼が書くべきだったと彼が考えていること、つまりHeap Layersを使用してヒープを構築する方法(本当にあなたのもの) を読んでください。HoardDieHard 、 DieHarder 、およびOOPLSA 2002 の論文Reconsidering Custom Memory Allocationで使用されているカスタム アロケータを構築するために Heap Layers を使用しました。

于 2011-01-19T00:09:51.040 に答える
1

Loki スモール オブジェクト アロケータを確認してください。

直接的な人間向けのドキュメントが得られなかったクイックグーグル。DOxygen で生成されたドキュメントがありますが、特に理解できるものではありません。ただし、設計と実装は Andrei Alexandrescu の「Modern C++ Design」に記載されています。

特定のクラスのオブジェクトの効率的なリサイクルが必要な場合は、単純な空きリスト (未加工のストレージ チャンクの空きリスト) を検討してください。

乾杯 & hth.,

于 2011-01-18T23:57:52.437 に答える
0

私が見る重要な問題は、

auto_ptr

デフォルト以外の方法で割り当てられたメモリの場合。適切なオーバーロードされた削除を定義することでこれを解決できますが、ファクトリの一部として独自の破棄関数を定義することをお勧めします。これを行うと、メモリ管理も Cache クラスにローカライズされ、そのクラスにローカルなパフォーマンスをより自由に改善できるようになります。もちろん、スマート ポインターを使用してメモリ管理を制御することをお勧めします。独自のアロケーターを定義し、それを使用するための smart_ptr を定義する必要があります。

参考までに、カスタム割り当てを管理するもう 1 つの方法は、カスタムの new 演算子を定義することです。つまり、このようなこと:

struct Cache
{
    void* allocate(size_t size)
    {
        size_t blockSize = sizeof(size_t) + size;
        // Placeholder: do what ever appropriate to blocks of size 'blockSize'
        return malloc(blockSize);
    }
    void destroy(void* p)
    {
        size_t* block = reinterpret_cast<size_t*>(p);
        size_t blockSize = *block;
        // Placeholder: do what ever appropriate to blocks of size 'blockSize'
        free(p);
    }

};
Cache cache;


void* operator new (size_t size, Cache& cache )
{
    return cache.allocate(size);
}

struct CacheObject 
{
    void operator delete(void* p)
    {
        cache.destroy(p);
    }
};


CacheObject* co = new (cache) CacheObject;
于 2011-01-19T00:09:20.577 に答える