繁体   English   中英

使用递增的整数作为键快速将值插入到地图中?

[英]Fast insertion of values into a map with an increasing integer as the key?

通过在参数位置提供适当的值,可以显着提高map::insert(iterator position, const value& k)效率。

如果我使用整数作为键,并且每次插入都使用大于所有先前插入的键的数字,那么在给出map的::end()迭代器时,我可以加快::insert操作吗?

就像是:

myMap.insert( myMap.end() , make_pair( next_number , myValue ) );

其中myMap的类型为map<uint64_t,MyType>next_number是每个递增的大整数。

编辑:

这个问题的答案可能会有所不同,具体取决于map存储的数据是否密集(请参阅下面的讨论)。 所以,让我们用两种方式提出这个问题:一旦它不密集,它就会变得密集。 还好奇。 也许测量会回答它。

为了直接回答问题,C ++规范说:

  • 在C ++ 03中,如果 p 之后插入t则插入带有a.insert(p,t)的映射必须分摊常量复杂度(而不是对数)。
  • 在C ++ 11中,如果 p 之前插入t则必须将带有a.insert(p,t)的映射插入到常量复杂度中。

在任何情况下, p都不需要可解除引用。 因此,在您的情况下, a.end()可能是C ++ 11中的最佳提示,但在C ++ 03中则不然。

我建议两件事:

  • 在这种情况下,更喜欢std::unordered_map ,总是在一端插入是红黑树的最坏情况
  • 如果new证明是一个麻烦,使用自定义分配器,从你所说的池分配策略可以使用

请注意,C ++ 11允许使用有状态分配器,因此应该很容易提供一个适合并且内部嵌入了std::vector<T>的分配器并将其用作堆栈。

任何建议都只是一个建议,需要尝试和衡量。 我们无法真正告诉您最高效的插入方式,您应该根据自己的具体用例进行测量,并了解最新情况。

如果您的地图紧凑且密集(几乎所有来自0 - max键的项目都被实际数据占用)并且最大键足够低以成为合理的数组索引,您可以切换到使用std::vector<value>并始终插入到最后。 由于它不断增长,你偶尔需要重新分配矢量(通常这是矢量加倍时)。 这可能很昂贵,但通常插入将非常便宜。 您不必处理二叉树的潜在重新平衡,并且向量对于其他目的而言非常缓存。

如果你的地图的密钥空间不紧凑/密集且最大密钥太大而不是可以想象的内存索引,那么插入一个提示将是你最好的选择。

如果顺序无关紧要,可以试试std :: unordered_map 这是一个哈希表实现。 因此插入成本将与散列的质量和速度相关。 使用64位密钥并将其转换为size_t散列(size_t甚至可能是64位)应该是微不足道的。

但是不必接受我的话,衡量它,亲眼看看......

自从我最近遇到这个问题以来,我做了一些测量。

我有一张大地图,有很多数据,很少插入数据,99%的时间只是使用引用访问和修改。 但是,此数据最终必须保存到磁盘并加载回来。 像“使用无序地图”这样的解决方案似乎是一种廉价的快速做错方式,有序的地图对我来说是正确的方式,因为数据是有序的。 唯一的问题是从文件加载。

我想知道这个操作的实际成本是多少,以及如何加快它,所以,我测量:

// Example program
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <time.h>

std::vector<int> amount = {100, 1000, 10000, 100000, 1000000, 5000000};

int main()
{
  for(int j=0; j<amount.size(); j++) 
  {
    clock_t tStart = clock();

    std::map<int,int> mymap;
    for(int i=0; i<amount[j]; i++){
      mymap[i] = i;
    }

    printf("Time taken []: %.2fs\n", (double)(clock() - tStart));
  }
  for(int j=0; j<amount.size(); j++) 
  {
    clock_t tStart = clock();

    std::map<int,int> mymap;
    mymap[0] = 0;
    auto it = mymap.begin();
    for(int i=1; i<amount[j]; i++){
      it = mymap.insert(it, std::pair<int,int>(i,i));
    }

    printf("Time taken insert end()-1: %.2fns\n", (double)(clock() - tStart));
  }
  for(int j=0; j<amount.size(); j++) 
  {
    clock_t tStart = clock();

    std::map<int,int> mymap;
    for(int i=1; i<amount[j]; i++){
      mymap.insert(mymap.end(), std::pair<int,int>(i,i));
    }

    printf("Time taken insert end(): %.2fns\n", (double)(clock() - tStart));
  }
  for(int j=0; j<amount.size(); j++) 
  {
    clock_t tStart = clock();

    std::map<int,int> mymap;
    for(int i=0; i<amount[j]; i++){
      mymap.insert(mymap.begin(), std::pair<int,int>(i,i));
    }

    printf("Time taken insert begin(): %.2fs\n", (double)(clock() - tStart));
  }
  return 0;
}

结果:

Time in ns
N       end()-1 end()   begin() []
100     12      8       22      12
1000    77      54      188     97
10000   763     532     2550    1174
100000  7609    6042    23612   17164
1000000 75561   62048   270476  272099
5000000 362463  306412  1827807 1687904

在此输入图像描述 在此输入图像描述

摘要:

  • 是的,有收益,巨大收益,没有任何真正的缺点。 在订购数据时,它比无序地图要好得多,对于将地图保存到地图并重新创建它非常有用。

  • 无论元素数量多少,如果提示正确,则插入时间相同。 因此,无需重复哈希无序映射以获得恒定时间。

  • 最糟糕的情况是,如果你的提示是最糟糕的提示,你可能会松一些。 我没有任何意义在没有提示的情况下进行插入,特别是如果您知道数据的插入位置。 而且大多数时候你这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM