我的应用程序目前性能非常关键,每帧请求350万个对象。最初,为了使球滚动,我new'd所有的东西,并使应用程序工作和测试我的算法。应用程序是多线程的.
一旦我对性能感到满意,我就开始为我的对象创建一个内存管理器。显而易见的原因是内存碎片和浪费。由于内存碎片,应用程序在崩溃之前不能继续超过几个帧。我已经检查了内存泄漏,并且知道应用程序是无泄漏的。
因此,我开始使用TBB的concurrent_queue创建一个简单的内存管理器。队列存储应用程序允许使用的最大元素集。需要新元素的类会从队列中弹出元素。根据英特尔的文档,try_pop方法是无锁的.就内存消耗而言,这是非常有效的(虽然仍然存在内存碎片,但没有以前那么多)。我现在面临的问题是,根据我自己的简单分析器,应用程序的性能下降了大约4次(我无法访问商业分析器,也不知道任何将在实时应用程序上工作的).请提出任何建议)。
我的问题是,是否有一个线程安全的内存池是可伸缩的。池的一个must-have特性是元素的快速循环并使其可用。如果没有,在性能方面有什么提示/技巧吗?
编辑:,我想我会更多地解释这个问题。我可以很容易地初始化n个数组,其中n是线程数,然后开始使用每个线程数组中的对象。这将完美地适用于某些情况。在我的例子中,我也在回收元素(可能是每一个帧),它们可以在数组的任意点被回收;也就是说,它可能来自数组的elementArray[0]、elementArray[10]或elementArray[1000]部分。现在,我将有一个由准备使用的元素和正在使用的元素组成的碎片数组:(
发布于 2011-04-21 00:21:24
正如注释中所说的,不要获得线程安全内存分配器,每个线程分配内存.
正如您在更新中所暗示的,您需要有效地管理免费/在使用中。这是一个非常直截了当的问题,考虑到一个常量类型,并且没有并发性。
例如(从我的头顶,未经测试):
template<typename T>
class ThreadStorage
{
std::vector<T> m_objs;
std::vector<size_t> m_avail;
public:
explicit ThreadStorage(size_t count) : m_objs(count, T()) {
m_avail.reserve(count);
for (size_t i = 0; i < count; ++i) m_avail.push_back(i);
}
T* alloc() {
T* retval = &m_objs[0] + m_avail.back();
m_avail.pop_back();
return retval;
}
void free(T* p) {
*p = T(); // Assuming this is enough destruction.
m_avail.push_back(p - &m_objs[0]);
}
};然后,对于每个线程,有一个ThreadStorage实例,并根据需要调用alloc()和free()。
您可以添加智能指针来管理对free()的调用,如果代价高昂,还可以优化构造函数/析构函数调用。
您还可以查看boost::pool。
更新:
新的要求,保持跟踪的东西已经被使用,以便他们可以在第二次处理,在我看来有点不清楚。我认为您的意思是,当主处理在对象上完成时,您不需要释放它,而是在第二阶段处理中保留对它的引用。有些对象将被释放回池,而不用于第二阶段的处理。
我想你想要在同一个线程中完成这个任务。
首先,您可以将这样的方法添加到ThreadStorage中,当您想要对所有未发布的T.实例进行处理时调用它,不需要额外的簿记。
void do_processing(boost::function<void (T* p)> const& f) {
std::sort(m_avail.begin(), m_avail.end());
size_t o = 0;
for (size_t i = 0; i != m_avail.size(); ++i) {
if (o < m_avail[i]) {
do {
f(&m_objs[o]);
} while (++o < m_avail[i]);
++o;
} else of (o == m_avail[i])
++o;
}
for (; o < m_objs.size(); ++o) f(&m_objs[o]);
}假设没有其他线程使用ThreadStorage实例,这是合理的,因为它是设计为线程本地的。再一次,从我的头顶上,未经测试。
发布于 2011-04-21 09:34:53
谷歌的TCMalloc
TCMalloc为每个线程分配一个线程本地缓存。从线程本地缓存中满足小的分配。对象根据需要从中央数据结构移动到线程本地缓存,并使用定期垃圾收集将内存从线程本地缓存迁移回中央数据结构。
性能:
TCMalloc比glibc2.3malloc.ptmalloc2大约需要300纳秒才能在2.8 GHz P4 (对于小型对象)上执行malloc/free对。对于同一个操作对,TCMalloc实现大约需要50纳秒.
发布于 2011-04-21 07:23:17
您可能想看看jemalloc。
https://stackoverflow.com/questions/5737344
复制相似问题