C ++:什么更快 - 在hashmap或switch语句中查找?

时间:2011-07-28 14:22:20

标签: c++ performance hashmap switch-statement

我有一个代码模式,可以将一个整数转换为另一个整数。就像这样:

int t(int value) {
    switch (value) {
        case 1: return const_1;
        case 3: return const_2;
        case 4: return const_3;
        case 8: return const_4;
        default: return 0;
    }
}

目前有大约50个条目,也许稍后会有更多,但可能不会超过一百或两个。所有值都是预定义的,当然我可以按其值排序案例标签。所以问题是,什么会更快 - 这种方法或将其放入哈希映射(我无法访问std :: map,所以我说的是我的SDK中可用的自定义哈希映射)并在该表中执行查找?也许这有点过早优化,但是......但我只是需要你的意见。

提前致谢。

编辑:我的案例值将介于0到0xffff之间。并且关于哈希映射的更好的可读性。我不确定它是否真的具有更好的可读性,因为我仍然需要用值填充它,因此常量映射表仍然需要在我的代码中。

EDIT-2 :已经给出了许多有用的答案,非常感谢。我想在这里添加一些信息。我的哈希键是整数,我的整数哈希函数基本上只是一个带有积分溢出的乘法:

EXPORT_C __NAKED__ unsigned int DefaultHash::Integer(const int& /*aInt*/)
{
_asm mov edx, [esp+4]
_asm mov eax, 9E3779B9h
_asm mul dword ptr [edx]
_asm ret
}

所以它应该很快。

10 个答案:

答案 0 :(得分:23)

switch构造更快(或至少不慢)。

这主要是因为switch构造为编译器提供了静态数据,而像哈希映射这样的运行时结构却没有。

当可能的编译器应该将switch构造编译成代码指针数组时:数组的每个项目(由索引索引)指向关联的代码。在运行时,这需要O(1),而哈希映射可能需要更多:平均情况下的O(log n)或最坏情况下的O(n),通常,并且无论如何都是更大的常数内存访问。

答案 1 :(得分:5)

我将加5美分:

对于大约50个条目的数量,std :: unordered_map(基于散列,O(1))通常比std :: map(基于树的O(ln(N)))慢,并且它们都比较慢然后在这种情况下我倾向于使用boost :: flat_map(排序向量O(ln(N)))。 并不总是可以将switch编译为跳转表,并且当它是,您可以简单地将您的值(或函数)放在向量中并通过索引访问。否则,switch比boost :: flat_map快一点。

请注意开头的“通常”一词,如果您关心这段代码的性能,请进行分析(并与我们分享结果:))。

答案 2 :(得分:4)

switch statement比在哈希映射中查找更快。

但是,如果您更改映射,映射将导致更易读的代码。通过从文件中读取结果,您可以使用地图轻松完成此操作。在switch语句中,您必须更改代码并重新编译。

答案 3 :(得分:3)

开关会更快。如果是少数情况,如在您的示例中,它将使用if-chain。如果有大量的情况,并且它们相当紧凑,它可以选择生成跳转表,只需要几条指令。 (顺便说一句,你不必订购案例。)哈希映射是O(1),但可能会占用10-40条指令。

答案 4 :(得分:2)

你可能会喜欢这篇文章: http://www.codeproject.com/KB/cpp/switch.aspx

答案 5 :(得分:2)

根据定义,数组将具有最快的访问时间。

switch语句比较值,然后使用跳转表(这是一个函数指针数组)。

hashmap 计算数据中的哈希值,然后搜索内存中的树或使用哈希值作为数组的索引。由于计算哈希值而变慢。

在大多数现代平台上,64k不是大量数据,可以静态分配为常量数组。

阵列技术的一个问题是考虑到您未考虑的密钥。一个例子是使用唯一的sentinel值。返回值时,您知道您有一个未知密钥。

我建议使用static const数组值。

答案 6 :(得分:2)

哈希映射的速度取决于两件事:哈希函数的速度和冲突的数量。当提前知道所有值时,可以创建没有冲突的perfect hash function。如果你可以生成一个只包含几个算术运算的完美哈希函数,那么它可能比开关更快。

答案 7 :(得分:2)

我同意使用阵列,但我没有投票支持它的声誉。它只有65536个条目,所以除非你受到严重的内存限制和/或你正在返回非常大的东西,而不是像你的例子那样使用静态const数组,你会好得多。拥有一个64k int的数组通常只有256kB,如果你可以使用short或char,它将是那个大小的一半或1/4。我认为使用switch语句可以获得的最佳效果是对于其代码指针数组之外的值的条件分支,以及第二个条件分支,以跳转到代码中的值以获取数组内部的值。能够只执行“return my_array [value]”只会导致内存提取(可能来自l3缓存)。

为了便于阅读,您可以将数组放在自己的文件中,并将网格中的所有值排成一行,每行10个或16个条目。然后用每个条目编号的第一部分(例如“// 0x12A?”)对每一行进行注释,并使用与列对齐的定期注释行来填入条目编号的最后一位数字(例如“// 0 1 2 3 4 5 6 7 8 9 ABCDEF“)。我已经为256个条目的几个数组做了这个,这比一个switch语句更容易管理。我还使用了具有64k条目的数组来实现快速整数对数,这使得管理变得更加复杂,但我能够编写一个程序来生成所有数组代码。

对于大的东西,在处理更多条目之前,代码管理可能并不容易,但这取决于您的编辑和技能。维护这样一个数组只是调整图表中的一个点,而不是寻找可能存在或不存在于“情况1:返回const_1;”的长列表中的值。一些for循环应该足以生成一个64k条目的数组,这些条目被正确评论并填充默认值。

为了安全起见,您可以考虑使用某种边界检查。这可以通过boost的前提条件,如果数字超出界限抛出异常或特殊返回,或者简单的“返回my_array [value& 0xffff]”来完成。但是,您可能对输入值有足够的保证,而您不需要任何输入值。

答案 8 :(得分:1)

我认为哪个更快会更明显。您可能需要分析这两种方法。

哈希映射应该具有O(1)的复杂性。

交换机(使用与您类似的非连续键)可以优化为二进制搜索(至少使用GCC),其复杂度为O(log n)。

另一方面,在哈希映射上执行的任何操作都将比在交换机中完成的操作昂贵得多。

答案 9 :(得分:1)

当不考虑碰撞时,哈希表时间复杂度一般为O(1)。 C ++标准没有规定如何实现切换,但它可以实现为跳转表,时间复杂度也是O(1),或者它可以实现为二进制搜索,时间复杂度为O(log n)或组合取决于多少案例陈述等。

总而言之,像您的情况一样小规模,切换速度更快,但哈希表可能会大规模获胜