使用预排序数据加载STL集,C ++

时间:2011-03-23 20:30:46

标签: c++ stl set sorted

我正在使用Visual Studio 2010中的C ++。我有一个STL集,我在程序关闭时保存到文件。下次程序启动时,我将(已排序)数据加载回一个集合中。我正在尝试优化加载过程,我遇到了麻烦。我怀疑问题是经常重新平衡,我正在寻找一种方法来避免这种情况。

首先,我没有进行优化,使用“set-> insert(const value_type& x)”

时间: ~5.5分钟

然后我尝试使用insert()的版本,在其中传递insert()的位置提示:

iterator insert ( iterator position, const value_type& x );

粗略地说,我这样做了:

set<int> My_Set;
set<int>::iterator It;
It = My_Set.insert (0);
for (int I=1; I<1000; I++) {
   It = My_Set.insert (It, I);  //Remember the previous insertion's iterator
   }

时间: ~5.4分钟

几乎没有任何进步!我不认为问题是从文件读取开销 - 注释掉insert()会将时间减少到2秒。我认为问题不在于复制对象的开销 - 它是一个带有int和char的Plain Old Data对象。

我能想到的唯一一点就是这套装置不断重新平衡。

1。)你同意我的猜测吗?

2。)有没有办法在我加载集合时“暂停”重新平衡,然后在结束时重新平衡一次? (或者......这会有帮助吗?)

3.。)是否有更智能的方法来加载已排序的数据,即不是简单地从最低到最高?也许交替我的插入,以便它不必经常平衡? (例如:插入1,1000,2,999,3,998 ......)

3 个答案:

答案 0 :(得分:6)

关于我们谈论的元素数量?

我用10.000.000整数(用向量准备)做了一个简短的测试,并以三种不同的方式将它们插入到集合中。

准备输入:

  std::vector<int> input;
  for(int i = 0; i < 10*1000*1000; ++i) {
     input.push_back(i);
  }

<小时/> 逐项插入设置项:

发布:2,4秒/调试:110,8秒

  std::set<int> mySet;
  std::for_each(input.cbegin(), input.cend(), [&mySet] (int value) {
     mySet.insert(value);
  });

<小时/> 使用insert(itBegin, itEnd)插入到集合中:

发布:0,9秒/调试:47,5秒

  std::set<int> mySet;
  mySet.insert(input.cbegin(), input.cend());

  // this is also possible - same execution time:
  std::set<int> mySet(input.cbegin(), input.cend());

因此插入可能会大幅加速,但即使是缓慢的方式也应该远离几分钟。

<小时/> 修改

我同时使用调试模式进行了测试 - 哇 - 我知道调试成本的性能,但它比我想象的要多。使用50.000.000元素在调试模式下有一个错误的alloc,所以我将我的帖子更新为10.000.000元素,并显示了发布和调试版本的时间。

你可以在这里看到巨大的差异 - 使用更快的解决方案50次。

此外,快速解决方案(insert(itBegin, itEnd))似乎与元素数量呈线性关系(使用预先排序的数据!)。普通测试的元素数量增加了五倍,插入时间从4,6减少到0.9,大约是5倍。

答案 1 :(得分:2)

您是否尝试过范围构造函数?

#include <set>
#include <fstream>
#include <algorithm>
#include <iterator>

int main()
{
    std::ifstream  file("Plop");

    std::set<int>   myset;

    std::copy(std::istream_iterator<int>(file),
              std::istream_iterator<int>(),
              std::inserter(myset, myset.end()));
}

用[0 - &gt;尝试了4种技术。 10,000,000个项目(按文件排序):

void t1(std::set<int>& data, std::istream& file)
{
    int x;
    while(file >> x)    {data.insert(x); }
}

void t2(std::set<int>& data, std::istream& file)
{
    int x;
    while(file >> x)    {data.insert(data.end(), x);}
}

void t3(std::set<int>& data, std::istream& file)
{
    std::set<int>::iterator it = data.begin();
    int x;
    while(file >> x)    {it = data.insert(it, x);}
}

void t4(std::set<int>& data, std::istream& file)
{
    std::copy(std::istream_iterator<int>(file),
              std::istream_iterator<int>(), 
              std::inserter(data, data.end()));
}

clock()中的时间平均超过3次运行(正常)和3次运行(-O4)

                    Plain Data
           Normal              -O4
           =========           ========= 
t1 Result: 21057300            6748061
t2 Result:  6580081            4752549
t3 Result:  6675929            4786003
t4 Result:  8452749            6460603

结论1:对于排序数据:

Best:   data.insert(data.end(), <item>)  // Hint end()
Worst:  data.insert(<item>);             // No Hint

结论2:优化计数。

答案 2 :(得分:1)

该集可能是重新平衡的。你真的有多少项需要5.6分钟?如果您的项目集足够大,则可能会遇到物理RAM限制和颠簸,或者只是存在非常糟糕的缓存未命中。

绝对没有办法禁用重新平衡。如果可以,那么该集合将能够打破其不变量,这将是不好的。

  • 获取一个分析器并对您的代码进行分析,而不是猜猜花了多少时间。
  • 您是否尝试使用end而不是之前的迭代器作为另一个数据点来使用两个参数插入?
  • 您是否尝试插入预先保留的vector而不是比较时间?
  • 你能逃脱另一种容器类型,如堆或(已排序)向量吗?
  • 如果您可以快速加载到矢量中,请执行此操作,然后random_shuffle,然后再次尝试插入集合中,看看会发生什么。