Std::设置快与慢,是怎么回事

std::set fast and slow, what is going on?

本文关键字:怎么回事 设置 Std      更新时间:2023-10-16

我发现std::set有一个奇怪的行为。

代码如下:

#include <cstdio>
#include <windows.h>
#include <stdlib.h>
#include <vector>
#include <set>
using namespace std;
int main(int argc, char *argv[])
{
    set<int> b[100];
    for (int o=0; o<10; o++)
    {
        int tt = GetTickCount();
        for (int i=0; i<5000000; i++)
        {
            b[o].insert(i);
        }
        tt = GetTickCount() - tt;
        b[o].clear();
        printf("%dn", tt);
    }
    return 0;
}

我用的是Windows XP。

下面是有趣的部分:第一次打印时间约为3500毫秒,而接下来的所有打印时间都超过9000毫秒!为什么会这样呢?

哦,这只发生在发布版本(-O2优化)。

这在Linux上不会发生(在修改代码以在那里编译之后)。

还有一件事:当我用Intel VTune运行它时,它总是需要大约3000毫秒,所以它应该是这样的。

更新:下面是一些新代码:

#include <cstdio>
#include <windows.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
const int count = 10000000;
int **a = new int*[count];
for (int o=0; o<10; o++)
{
    int ttt = GetTickCount();
    for (int i=0; i<count; i++)
    {
        a[i] = new int;
        *a[i] = i;
    }
    int ttt2 = GetTickCount();
    for (int i=0; i<count; i++)
    {
        int r1 = rand() * 10000 + rand();
        int r2 = rand() * 10000 + rand();
        r1 = r1%count;
        r2 = r2%count;
        int *e = a[r1];
        a[r1] = a[r2];
        a[r2] = e;
    }
    int ttt3 = GetTickCount();
    for (int i=0; i<count; i++)
    {
        delete a[i];
    }
    int ttt4 = GetTickCount();
    printf("%d %dn", ttt2-ttt, ttt4-ttt3);
}
return 0;
}

这是同样的问题。实际情况是,我分配了很多很多小对象,然后以随机顺序删除它们——这与std::set中的情况类似。这就是Windows内存管理问题。它不能很好地处理许多小的分配和删除。

我不能确切地解释为什么会发生这种情况,但我可以提出一个解决方案。当我在调试器下运行发布构建(使用F5)时,我已经能够在我的PC上复制这一点。当我从命令行或Ctrl-F5运行构建时,我没有得到那种行为。

这与在调试器下启动时默认打开的调试堆有关。这里有非常详细的描述。要防止这种情况发生

  1. 从命令行或Ctrl-F5运行(Debug -> Start Without Debugging)
  2. 进入Project -> Properties -> Debugging -> Environment,添加_NO_DEBUG_HEAP=1

如果我不得不猜测,我会说这与Windows/VS运行时内存分配跟踪的实现有关。可能是一些内部列表填满了,重新分配了,或者发生了其他事情。

我认为std::set是作为二叉搜索树实现的。由于每次都将i增加1,因此实际上为这种类型的数据结构创建了一个对抗性(最坏情况)场景(几乎每次插入都需要重新平衡树)。

同样,它有5000万次插入,所以预计会有一些时间,尽管我不认为它会是5毫秒。

此外,我会在您打印时间后执行您的"清除",因为我不明白您为什么要对插入和删除项进行基准测试。