我的应用程序目前性能非常高,每帧需要3-5万个对象。最初,为了让球滚动,我new'd
一切,让应用程序工作并测试我的算法。该应用程序是多线程的。
一旦我对性能感到满意,我就开始为我的对象创建一个内存管理器。显而易见的原因是内存碎片和浪费。由于内存碎片,应用程序在崩溃之前无法继续超过几帧。我检查了内存泄漏,并知道应用程序是无泄漏的。
所以我开始使用TBB的concurrent_queue
创建一个简单的内存管理器。队列存储允许应用程序使用的最大元素集。需要新元素的类弹出队列中的元素。根据英特尔的文档,try_pop
方法是无锁的。就内存消耗而言,这种方法效果很好(尽管仍然存在内存碎片,但并不像以前那么多)。我现在面临的问题是,根据我自己的简单分析器,应用程序的性能已经放慢了大约4倍(我无法访问商业分析器或知道任何可用于实时应用程序的任何建议...任何建议将不胜感激。
我的问题是,是否存在可扩展的线程安全内存池。池中的must-have
功能是快速回收元素并使其可用。如果没有,任何提示/技巧表现明智吗?
编辑:我以为我会更多地解释这个问题。我可以轻松初始化 n 数组,其中 n 是线程数,并开始使用每个线程数组中的对象。这对某些情况非常有效。就我而言,我也在回收元素(可能是每一帧),它们可以在阵列中的任何一点进行回收;即它可能来自阵列的elementArray[0]
或elementArray[10]
或elementArray[1000]
部分。现在我将有一个碎片阵列的元素,包括可以使用的元素和正在使用的元素:(
答案 0 :(得分:5)
如评论中所述,不要获得线程安全的内存分配器,为每个线程分配内存。
正如您在更新中暗示的那样,您需要有效管理免费/使用中。这是一个非常简单的问题,给定一个常量类型,没有并发性。
例如(在我的头顶,未经测试):
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实例,这是合理的,因为它是线程本地的设计。再次,脱离我的头顶,未经测试。
答案 1 :(得分:2)
您可能需要查看jemalloc。
答案 2 :(得分:2)
Google的TCMalloc,
TCMalloc为每个线程分配一个 线程本地缓存。小额拨款 从本地线程满意 缓存。对象从中心移动 数据结构成为线程局部的 根据需要缓存,并定期垃圾 集合用于迁移内存 从线程本地缓存返回到 中央数据结构。
性能:
TCMalloc比glibc 2.3 malloc更快... ptmalloc2需要大约300纳秒才能在2.8 GHz P4上执行malloc / free对(对于小对象)。对于相同的操作对,TCMalloc实现大约需要50纳秒......