[英]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 ++规范说:
p
之后插入t
则插入带有a.insert(p,t)
的映射必须分摊常量复杂度(而不是对数)。 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.