为什么在MSVC 2010下循环中的new和delete速度非常慢

4

当我尝试在循环中创建和删除一个类的实例时,出现了问题。迭代的执行时间非常不同。据我所知,这与从内存中删除对象有关。然而,我不理解这个操作的行为。为什么时间不同?我该怎么修复它?当我在单独的线程中删除对象时,时间是稳定的。

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;
    }
}

新增内容: 我很困惑。当我在VS之外运行程序时,它可以正常工作...


5
动态分配很慢,好吧? - Lightness Races in Orbit
2
此外,您正在创建120,000个节点项。您运行30次创建操作,每次创建操作会生成200个子节点,即6,000个对象,然后每个子节点又会创建20个节点,总共是120,000个项。 - taylonr
1
供参考,同一用户的相关(几乎重复)问题:http://stackoverflow.com/questions/5713329/why-does-it-look-like-boostshared-ptr-constructions-are-getting-slower - forsvarir
5个回答

5
当您创建对象时,有时会为其分配新的内存块,有时会适合已经存在的块中。这将导致两个分配可能花费不同的时间。
如果您想使时间分配和释放保持一致,请在应用程序内处理它们-获取大块内存并从该块中服务分配。当然,当您需要另一个块时,导致发生这种情况的分配将需要更长时间...但是使用大块应该很少发生。
使分配和释放采取完全一致的时间的唯一方法是减慢快速分配,直到它们花费任何请求可以花费的最长时间。这将是对性能优化的Harrison Bergeron方法。我不推荐这样做。

为什么当我在单独的线程中删除对象时,时间是稳定的。 - user713782

1

存在实时堆,但通常内存堆操作(动态分配和释放)是非确定性操作的典型示例。这意味着运行时会变化,并且甚至没有很好的限制。

问题在于,当相邻的内存块发生时,通常需要将它们合并为单个块。如果不这样做,最终你将只拥有许多微小的块,即使实际上有足够的内存可用于大型分配,也可能失败。对于任何给定的调用,可能需要或者不需要进行合并,需要进行的数量可能会有所变化。这完全取决于您的系统最近执行的分配/释放模式,这是您无法计划的事情。因此,我们称之为“非确定性的”。

如果您不喜欢这种行为,实际上有两种可能性:

  1. 切换到使用实时堆。您的操作系统可能没有内置的实时堆,因此您需要购买或下载一个,并将其用于所有内存操作。我过去使用过的一个是TLSF
  2. 不要在主循环中执行动态内存分配/释放(即:初始化后不要这样做)。这是我们实时程序员训练自己编写代码的方式。

循环只是一个例子,我在真正的代码中没有它。但为什么差异如此之大? - user713782
@user713782 - 再次强调,这完全取决于分配/释放的模式以及您的系统堆需要多少工作来将所有内容合并为其所需的方式。导致最大工作量的确切原因取决于所使用的算法。 - T.E.D.
好的。但它解释了在单独的线程中使用解除分配时时间不会增加的情况吗? - user713782
@user713782 - 为什么会这样呢?通常一个进程中的所有线程都使用同一个堆。 - T.E.D.

0
通常情况下,无法预测内存分配/释放的时间。例如,如果用户空间堆耗尽页面并需要从内核请求更多页面,以及稍后访问新分配的页面触发页面错误时,时间可能会有很大差异。
因此,即使您继续使用大块内存实现自己的堆,您的分配时间也会因为懒惰是底层内存系统本质的原因而有所不同。

通过使用mlock锁定到主内存中的页面就足以防止惰性分配。所有开销都将在分配块时而不是以后产生。 - Borealid
我理解,但在这个例子中,时间差可以是x * 10。如果我不使用delete,在新的内存块中会创建一个新对象,时间也不会增加。为什么会发生这种情况? - user713782

0

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

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


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接