C++ 如何在C+;中实现可变大小的缓存对象以减少内存分配+;?

C++ 如何在C+;中实现可变大小的缓存对象以减少内存分配+;?,c++,caching,memory,memory-pool,C++,Caching,Memory,Memory Pool,在表演前,人们把我的头扯下来:是的,在问这个问题之前,我已经做了分析:) 我再次查看了我的,虽然我有一个可行的解决方案,但性能很差,因为缓存的每种类型的项都会导致堆上的单独分配(这当然很昂贵) 基于对程序输入的静态分析,我找到了一种方法,可以知道可能放入正在传递的缓存对象中的所有对象所需的总大小。基本上,我有一个可以在给定缓存对象中构造的对象列表,因此我提前知道可能需要缓存的对象的大小,但不是在编译时——仅在运行时 基本上,我想做的是boost::make_shared所做的事情——获取单个内存

在表演前,人们把我的头扯下来:是的,在问这个问题之前,我已经做了分析:)

我再次查看了我的,虽然我有一个可行的解决方案,但性能很差,因为缓存的每种类型的项都会导致堆上的单独分配(这当然很昂贵)

基于对程序输入的静态分析,我找到了一种方法,可以知道可能放入正在传递的缓存对象中的所有对象所需的总大小。基本上,我有一个可以在给定缓存对象中构造的对象列表,因此我提前知道可能需要缓存的对象的大小,但不是在编译时——仅在运行时

基本上,我想做的是
boost::make_shared
所做的事情——获取单个内存块,并在同一内存块中构造
shared_ptr
位以及受控对象

我不必担心保留复制行为,因为缓存对象是不可复制的,并且由客户端通过指针传递(它通常存储在
ptr\u向量
std::auto\u 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);
    }
};
//我知道这里有很多东西是不可移植的——我只需要在x86上运行
//和x64机器。是的,这两门课看起来很粗糙,但我宁愿
//有一个黑客类而不是一个完整的程序:)
类缓存注册器
{
//废话
公众:
//找出缓存中的对象等
常量std::vector&GetRequiredObjectSizes()常量;
//其他东西。。。
模板
void RegisterCacheObject();
模板
std::size\u t GetObjectIndex()常量;
//等等。
};
类缓存对象;
std::auto_ptr CacheObjectFactory(常量缓存注册器和注册器)
{
//假设这是在CPP文件中,因此定义了CacheObject。。。
const std::vector&size(register.getrequiredobjectsize());
std::size\t sumOfCache=std::累计(size.begin(),size.end());
sumOfCache+=sizeof(CacheObject);
boost::作用域数组缓冲区(新字符[]sumOfCache);
CacheObject*obj=new(reinterpret_cast(buffer.get()))CacheObject;
buffer.release();//伪代码(boost::scoped_数组没有发布成员);
return std::auto_ptr(obj);//Nothrow
}
类缓存对象
{
CacheRegistrar*registrar;//由我的构造函数设置
公众:
模板
T&Get()
{
char*startOfCache=reinterpret_cast(this)+
sizeof(CacheObject);
char*cacheItem=startOfCache+register->GetObjectIndex();
return*reinterpret_cast(缓存项);
}
};

我的一般概念在这里正确吗?有没有更好的方法来实现这一点?

查看Loki small objects分配器

快速的谷歌搜索并没有产生任何直接面向人类的文档。有DOxygen生成的文档,但不是特别容易找到。但是,Andrei Alexandrescu的“现代C++设计”中的设计和实现是有记载的。 胡:如果你只想对给定类的对象进行有效的循环,那么考虑一个简单的自由列表——可能是一个原始存储块的免费列表。
干杯,

查看Loki small objects分配器

快速的谷歌搜索并没有产生任何直接面向人类的文档。有DOxygen生成的文档,但不是特别容易找到。但是,Andrei Alexandrescu的“现代C++设计”中的设计和实现是有记载的。 胡:如果你只想对给定类的对象进行有效的循环,那么考虑一个简单的自由列表——可能是一个原始存储块的免费列表。
干杯,

我看到的关键问题是返回一个

自动检查

用于以非默认方式分配的内存。您可以通过定义合适的重载删除来解决这个问题,但最好将自己的销毁函数定义为工厂的一部分。如果这样做,还可以在缓存类中本地化内存管理,从而使您可以更自由地改进该类的本地性能。 当然,使用智能指针控制内存管理是一个好主意;您需要做的是定义自己的分配器并定义一个智能ptr来使用它

作为参考,管理自定义分配的另一种方法是定义自定义新运算符。也就是说,这类事情:

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;
struct缓存
{
作废*分配(大小\u t大小)
{
大小块大小=大小(大小块大小)+大小;
//占位符:对“blockSize”大小的块执行任何适当的操作
返回malloc(块大小);
}
无效销毁(无效*p)
{
尺寸*块=重新解释铸件(p);
大小\u t块大小=*块;
//占位符:对“blockSize”大小的块执行任何适当的操作
自由基(p);
}
};
缓存;
void*运算符新建(大小、缓存和缓存)
{
返回cache.allocate(大小);
}
结构缓存对象
{
void运算符删除(void*p)
{
销毁(p);
}
};
CacheObject*co=新(缓存)CacheObject;

我看到的关键问题是返回

自动检查

用于以非默认方式分配的内存。您可以通过定义合适的重载删除来解决这个问题,但最好将自己的销毁函数定义为工厂的一部分。如果这样做,还可以在缓存类中本地化内存管理,从而使您可以更自由地改进该类的本地性能。 当然,使用智能指针控制内存管理是一个好主意;您需要做的是定义自己的分配器并定义一个智能ptr来使用它

作为参考,管理自定义分配的另一种方法是定义自定义新运算符。即