更有效的结构如unordered_map <pair <int,int =“”>,int&gt; </pair <int,>

时间:2014-07-11 07:47:09

标签: c++ performance algorithm map unordered-map

我有大约20,000,000 pair<int, int>我需要与int相关联。我用unordered_map<pair<int, int>, int>这样做了。分析我的算法表明检查条目是否存在

bool exists = myMap[make_pair(a, b)] != NULL

是性能瓶颈。我认为从unordered_map检索此信息会非常快,因为它是 O(1)。但如果常数很大,恒定时间可能会很慢......

我的哈希函数是

template <>
struct tr1::hash<pair<int, int> > {
public:
        size_t operator()(pair<int, int> x) const throw() {
             size_t h = x.first * 1 + x.second * 100000;
             return h;
        }
};

你知道我的问题有更好的数据结构吗?

显然,我不能将信息存储在矩阵中,因此内存量不适合现有的任何计算机。我所知道的分布是myMap[make_pair(a, a)]对于任何a都不存在。并且所有int都在0到大约20,000,000的连续范围内。

将其视为稀疏的20,000,000x20,000,000-Matrix,大约有20,000,000个条目,但从不在主对角线上。

建议

vector<pair<int, int>>*(带有 N 条目的数组)预计会更快吗?查找a将是微不足道的(只是数组的索引),然后我将迭代向量,将对的first值与b进行比较。

BIG UPDATE

我上传了raw data,因此您可以看到结构。

5 个答案:

答案 0 :(得分:5)

您是否尝试过使用myMap.find(make_pair(a,b)) != myMap.end()?如果元素不存在,operator[]会创建该元素。我希望find更快。

答案 1 :(得分:3)

首先,myMap[make_pair(a, b)] != NULL没有按照您的想法行事。如果它不存在,则插入该对,并将映射的值与0(NULL扩展为)进行比较。它根本不检查是否存在。 (请注意,在现代C ++中,绝不能使用NULL。数字使用0,指针使用nullptr

至于主题,你的哈希函数似乎不太好。不要忘记int s上的算术是在int s中完成的。由于大多数编译器int是32位,因此其最大值略高于2,000,000,000。因此,20,000,000 * 10,000比这更大,导致溢出(和未定义的行为)。

鉴于您的数据数量,我假设您使用的是64位平台,这意味着size_t长度为64位。因此,使用这样的哈希函数可能会得到更好的结果:

size_t operator()(pair<int, int> x) const throw() {
     size_t f = x.first, s = x.second;
     return f << (CHAR_BIT * sizeof(size_t) / 2) | s;
}

这应该会产生与你现在拥有的冲突明显更少的冲突(并且已经定义了行为)。

如果这没有帮助,您还可以尝试两步法:

std::unordered_map<int, std::unordered_map<int, int>>

首先按x.first查找,然后按x.second查找。我不知道这是否会有所帮助;衡量和看。

答案 2 :(得分:2)

主要的是避免在每次搜索时添加默认构造的元素:

bool exists = myMap[make_pair(a, b)] != NULL; // OUCH

bool exists = myMap.find(make_pair(a, b)) != myMap.end();  // BETTER

iterator i = myMap.find(make_pair(a, b);
if (i != myMap.end()) ... else ...;      // MAY BE BEST - SEE BELOW

伟大的哈希挑战......呜啊!这可能值得一试,但很大程度上取决于对中的数字如何分配以及您的实现std::hash(通常是传递!):

    size_t operator()(pair<int, int> x) const throw() {
         size_t hf = std::hash(x.first);
         return (hf << 2) ^ (hf >> 2) ^ std::hash(x.second);
    }

如果用int64_t替换对,可能也会更快找到它,因此关键比较肯定是简单的整数比较而不是级联。

另外,在测试存在后你在做什么?如果您需要访问/更改与相同键关联的值,则应保存迭代器find返回并避免其他搜索。

答案 3 :(得分:1)

正如建议的那样,我选择了带有 N 条目的vector<pair<int, int>>*。它比unordered_map快了大约40%。

答案 4 :(得分:0)

我建议您使用更好的哈希函数进行测试。如果您在此处搜索SO,可以找到示例,但这是一种可能的实现方式。

struct pair_hash {
    template <typename T1, typename T2>
    size_t operator()(const std::pair<T1, T2> &pr) const {
        using std::hash;
        return hash<T1>()(pr.first) ^ hash<T2>()(pr.second);
    }
};