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来使用它
作为参考,管理自定义分配的另一种方法是定义自定义新运算符。即