unordered_map的顺序是否具有确定性?

时间:2018-02-11 19:59:07

标签: c++ dictionary deterministic unordered

我想知道是否可以保证unordered_map的顺序在所有CPU,线程等中总是相同的。

我意识到特定订单本身可能没有明显的模式(因此,'无序'映射),但是如果我在另一台机器上运行我的进程,或者连续多次运行我的进程,或者在不同的线程上运行,那么顺序是如果散列函数和插入顺序保持不变,插入的项总是相同的?换句话说,如果我的代码没有改变,我的进程的每次执行都会导致地图元素的顺序相同吗?

我已经进行了一些测试,插入后的项目顺序似乎每次都相同,但这可能只是一个侥幸,我只有这台机器可以测试。我需要知道订单是否会受到任何其他因素的影响,例如CPU /内存架构,操作系统(Windows 8与Windows 10)等。

2 个答案:

答案 0 :(得分:2)

TL; DR :可以这样做,但我不推荐它。如果可以,请使用其他数据结构;你自己的哈希表,“treap”,平面数组或其他东西。

std::unordered_map(或集合)中项目的顺序更依赖于标准库实现而不是硬件/ CPU /等。

出于这个原因,如果你在不同的硬件上使用相同的库实现,并提供自己的哈希函数(以确保它不是在运行中随机化 - 来对抗针对你的数据结构的DoS攻击),或者硬件或操作系统 - 依赖,那么你应该没事。

但是,如果您正在寻找标准中的保证,您将找不到任何保证。唯一相关的保证是在对象的同一实例中,相同的密钥将散列到同一个桶。我不认为即使对于不同的地图实例也有保证,而且我从(痛苦的)个人经验中知道在不同的应用程序运行中没有一致性。

但并非所有希望都失去了!如果你坚持unordered_map的相同实现,并使用自己的哈希函数,并查看实现,以确保没有隐藏的意外(任何依赖硬件/操作系统/时间/ RNG /等。应该相对容易发现)你可以管理它。

请注意,由于您似乎在Windows上并且可能使用MSVC,因此默认的unordered_map使用默认的散列算法在所有顺序中 - 在相同的已编译二进制文件的运行中保持一致(至少在2013/2015 IIRC没有。)

要记住的另一件事是,如果您认真对待一致性,则必须确保链接CRT 静态。如果您链接到DLL版本,一些未来的补丁/更新可能会在您发布后更改应用程序的行为。

答案 1 :(得分:1)

在不同的硬件平台上运行以下代码时,我得到了不同的输出顺序。

#include <iostream>
#include <set>
#include <unordered_set>
#include <vector>
#include <functional>
#include <string>

struct hash_func {
    std::size_t operator()(const std::string &key) const {
        std::size_t hash = 0U;
        const std::size_t mask = 0xF0000000;
        for (std::string::size_type i = 0; i < key.length(); ++i) {
            hash = (hash << 4U) + key[i];
            std::size_t x = hash & mask;
            if (x != 0)
                hash ^= (x >> 24);
            hash &= ~x;
        }
        std::cout << "for key " << key << " hash " << hash << std::endl;

        return hash;
    }
};

void show() {
    std::vector<std::string> v;
    v.push_back("n1YxyaBzoRogNh72eri7HBGijCAtcHpf9nm,");
    v.push_back("n1GV9UScgncwU6KKL9T18mCo2S6uAE69SWs,");
    v.push_back("n1X2SXyEKej7GZgAreXDCkiT59qaYKBDcYi,");

    std::unordered_set<std::string, hash_func> s;
    for (auto it = v.begin(); it != v.end(); it++) {
        s.insert(*it);
    }

    for (auto it = s.begin(); it != s.end(); it++) {
        std::cout << *it << std::endl;
    }
}

int main() {
    show();

    return 0;
}

结果是:

1)英特尔(R)至强(R)CPU E5-2680 v4 @ 2.40GHz

for key n1YxyaBzoRogNh72eri7HBGijCAtcHpf9nm, hash 43411516
for key n1GV9UScgncwU6KKL9T18mCo2S6uAE69SWs, hash 93983804
for key n1X2SXyEKej7GZgAreXDCkiT59qaYKBDcYi, hash 17984604
n1X2SXyEKej7GZgAreXDCkiT59qaYKBDcYi,
n1GV9UScgncwU6KKL9T18mCo2S6uAE69SWs,
n1YxyaBzoRogNh72eri7HBGijCAtcHpf9nm,

2)AMD EPYC 7501 32核处理器

for key n1YxyaBzoRogNh72eri7HBGijCAtcHpf9nm, hash 43411516
for key n1GV9UScgncwU6KKL9T18mCo2S6uAE69SWs, hash 93983804
for key n1X2SXyEKej7GZgAreXDCkiT59qaYKBDcYi, hash 17984604
n1X2SXyEKej7GZgAreXDCkiT59qaYKBDcYi,
n1YxyaBzoRogNh72eri7HBGijCAtcHpf9nm,
n1GV9UScgncwU6KKL9T18mCo2S6uAE69SWs,