为什么在MSVC 2010下新建和删除这么慢

Why new and delete so slow in a loop under MSVC 2010

本文关键字:删除 新建 MSVC 2010下 为什么      更新时间:2023-10-16

当我试图在循环中创建和删除类的实例时,遇到了一个问题。迭代的执行时间是完全不同的。据我所知,这与从记忆中删除对象有关。但是,我不理解这种操作行为。为什么时间不同?我怎么修理它?当我在一个单独的线程中删除对象时,时间是稳定的。

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。
  1. 不要在主循环中执行动态内存分配/释放(IOW:不要在初始化之后)。这就是我们实时程序员多年来训练自己编写代码的方式。

除了其他答案所说的,您还必须考虑visualc++ 10运行时堆是线程安全的。这意味着,即使只有一个线程,也会遇到一些开销,以促进线程安全的堆操作。因此,您看到如此糟糕的结果的一个原因是您使用了通用但相当慢的堆实现。

使用/不使用调试器获得不同时间的原因是当程序在调试器下启动时使用了一个特殊的堆(即使在发布配置中),并且该特殊堆也相对较慢。

通常不可能预测内存分配/释放时间。例如,如果用户空间堆的页面用完,需要从内核请求更多的页面,或者稍后访问新分配的页面触发页面错误,那么时间可能会有很大的变化。

因此,即使您继续使用大块内存实现自己的堆,您的分配时间也会有所不同,因为底层内存系统的本质是懒惰。

如果你真的想知道为什么它慢,你需要在它上面运行一个真正的分析器,比如AMD的代码分析器,clock并不是一个精确的计时器。

每次运行不同的原因取决于底层系统的分页、CPU负载以及处理器是否缓存了数据。