一个人将如何实现可变大小的缓存对象来减少C ++中的内存分配?
-
12-10-2019 - |
题
在表演之前,人们撕开了我的头:是的,我在问这个:)之前已经进行了分析::)
我再次看着我的 类型容器之一, ,尽管我有一个可行的解决方案,但性能很差,因为缓存的每种类型的项目都会在堆上单独分配(当然很昂贵)。
基于对程序输入的静态分析,我发现了一种方法,可以知道所有可能放在我的缓存对象中所需的对象所需的总尺寸。基本上,我有一个可以在给定的缓存对象中构造的对象列表,因此我知道我可能要提前缓存的大小,但不在编译时 - 仅在运行时。
基本上,我想做的是 boost::make_shared
确实 - 获取一个内存块,并构造 shared_ptr
位以及同一内存块中的受控对象。
我不必担心保留复制行为,因为缓存对象是不可复制的,并且由客户指针传递(通常存储在类似的东西中 ptr_vector
或a 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);
}
};
我的一般概念在这里听起来吗?有更好的方法吗?
解决方案
但首先,阅读 这篇文章Andrei Alexandrescu 关于他认为他应该在该章中写的内容 - 一种使用 堆层 (真的是你的)。我用堆层建造 囤, 死硬死硬的顽固的, , 和 迪哈德, ,以及我们的OOPLSA 2002论文中使用的自定义分配器, 重新考虑自定义内存分配, ,在开始创建自定义分配器之前,您也应该阅读。
其他提示
查看Loki小对象分配器。
快速竞争不会产生任何直接面向人类的文档。有Doxygen生成的文档,但并非特别可抓。但是,在Andrei Alexandrescu的“现代C ++设计”中记录了设计和实施。
如果您只想有效地回收给定类的对象,请考虑一个简单的自由列表 - 可能是一个自由列表的原始存储块。
欢呼和hth。
我看到的关键问题是返回
auto_ptr
对于以非默认方式分配的记忆。您可以通过定义合适的过载删除来解决此问题,但最好将自己的销毁功能定义为工厂的一部分。如果执行此操作,您还将内部内存管理本地化在缓存类中,从而使您有更多自由来提高该类别的本地性能。如果当然,使用智能指针来控制内存管理是一个好主意;您需要做的是定义自己的分配器并定义SMART_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;