为什么在MSVC 2010下新建和删除这么慢
Why new and delete so slow in a loop under MSVC 2010
当我试图在循环中创建和删除类的实例时,遇到了一个问题。迭代的执行时间是完全不同的。据我所知,这与从记忆中删除对象有关。但是,我不理解这种操作行为。为什么时间不同?我怎么修理它?当我在一个单独的线程中删除对象时,时间是稳定的。
class NODE{
public:
NODE(){}
NODE* add(NODE* node)
{
children.push_back(node);
return node;
}
virtual ~NODE()
{
for(vector<NODE*>::iterator it = children.begin(); it != children.end(); ++it)
{
delete *it;
}
}
vector<NODE*> children;
};
NODE* create()
{
NODE* node( new NODE() );
for (int i=0; i<200;i++) {
NODE* subnode = node->add( new NODE());
for (int k=0; k<20; k++) subnode->add( new NODE());
}
return node;
}
int main()
{
NODE* root;
unsigned t;
for (int i=0; i<30; i++){
t = clock();
cout << "Create... ";
root = create();
delete root;
cout<< clock()-t << endl;
}
}
补充道:我困惑。
当您创建一个对象时,有时会为它分配一个新的内存块,有时它会适合已经存在的块。这将导致两次分配可能花费不同的时间。
如果你想让时间分配和自由保持一致,在你的应用程序中处理它们——从那个块中获取一大块内存和服务分配。当然,当你需要另一个块时,导致它发生的分配将花费更长的时间……但是对于大块,这种情况应该很少发生。
使分配和释放占用完全一致的时间的唯一方法是减慢快速的分配,直到它们占用任何请求可能花费的最大时间。这就是Harrison Bergeron的性能优化方法。我不推荐。
存在实时堆,但通常内存堆操作(动态分配和释放)是不确定性操作的典型示例。这意味着运行时是变化的,甚至没有一个很好的边界。
问题是,当相邻的内存块出现时,您通常需要将它们合并为单个块。如果不这样做,最终您只会得到大量的小块,即使确实有足够的内存可用,大的分配也可能失败。对于任何给定的调用,可能有也可能没有合并要做,并且要做的数量可能会有所不同。这完全取决于系统最近执行的分配/释放模式,这根本不是您可以计划的。所以我们称之为"非确定性"。
如果你不喜欢这种行为,有两种可能:
- 切换到使用实时堆。你的操作系统可能没有内置一个,所以你必须去购买或下载一个,并使用它来进行所有的内存操作。我过去用过的一个是TLSF。
- 不要在主循环中执行动态内存分配/释放(IOW:不要在初始化之后)。这就是我们实时程序员多年来训练自己编写代码的方式。
除了其他答案所说的,您还必须考虑visualc++ 10运行时堆是线程安全的。这意味着,即使只有一个线程,也会遇到一些开销,以促进线程安全的堆操作。因此,您看到如此糟糕的结果的一个原因是您使用了通用但相当慢的堆实现。
使用/不使用调试器获得不同时间的原因是当程序在调试器下启动时使用了一个特殊的堆(即使在发布配置中),并且该特殊堆也相对较慢。
通常不可能预测内存分配/释放时间。例如,如果用户空间堆的页面用完,需要从内核请求更多的页面,或者稍后访问新分配的页面触发页面错误,那么时间可能会有很大的变化。
因此,即使您继续使用大块内存实现自己的堆,您的分配时间也会有所不同,因为底层内存系统的本质是懒惰。
如果你真的想知道为什么它慢,你需要在它上面运行一个真正的分析器,比如AMD的代码分析器,clock
并不是一个精确的计时器。
每次运行不同的原因取决于底层系统的分页、CPU负载以及处理器是否缓存了数据。
- 创建模板类型而不新建/删除
- C++ 在请求特定字节的新建后删除
- 为什么非放置"新建"和"删除"内置于语言中,而不仅仅是常规函数?
- 来自C#的mingw DLL:为什么我必须覆盖新建/删除?
- C++ - 定义自定义新建和删除运算符时make_shared
- 使用安全零内存新建/删除时出现问题
- 奇怪的内存泄漏由C++中的新建/删除
- CRT 检测到应用程序在堆缓冲区(新建/删除)类结束后写入内存
- 无法覆盖C++中纯抽象类中的运算符删除/新建
- 编译器或标准C++库 - 新建和删除
- 混合运算符和表达式新建/删除
- 新建和删除的经验法则
- 运算符新建和删除重载作用域
- 使用运算符重载(新建/删除)实现单例的优缺点
- 忽略全局覆盖的新建/删除
- C++内存泄漏新建并删除
- 基本的新建/删除操作员日志记录
- C++自定义全局新建/删除覆盖系统库
- 为什么使用XXXX_new和XXXX_free而不是新建和删除
- 新建和删除如何工作以及它们的存储位置