切换案例或std :: map的效率更高

时间:2009-05-31 11:46:42

标签: c++ parsing tokenize

我正在考虑这里的标记器。
每个标记在解析器内调用不同的函数 什么更有效:

  • std :: functions / boost :: functions
  • 的地图
  • 开关盒

6 个答案:

答案 0 :(得分:31)

我建议您阅读Joel on Software上的switch() vs. lookup table?。特别是,这种反应很有意思:

  

“人们浪费时间的典范   试图优化最少   重要的是。“

     

是和否。通常,在VM中   调用每个人都做的微小功能   小。这不是通话/回程   这和前言一样伤害了你   每个功能的清理程序   往往是一个很大的百分比   执行时间。这一直是   研究死亡,特别是通过   已实现线程化的人   解释器。

在虚拟机中,存储计算地址以进行调用的查找表通常优先于交换机。 (直接线程或“标签为值”。直接调用存储在查找表中的标签地址)这是因为它允许在某些条件下减少branch misprediction,这在长流水线CPU中非常昂贵(它强制冲洗管道)。但是,它使代码的可移植性降低。

此问题已在VM社区中进行了广泛讨论,如果您想了解更多相关信息,我建议您在此字段中查找学者论文。 Ertl& Gregg在2001年写了一篇关于这个主题的精彩文章,The Behavior of Efficient Virtual Machine Interpreters on Modern Architectures

但如上所述,我非常确定这些详细信息与您的代码无关。这些都是小细节,你不应该过分关注它。 Python解释器使用开关,因为他们认为它使代码更具可读性。你为什么不选择最舒服的用法?性能影响相当小,您现在最好关注代码可读性;)

编辑:如果重要,使用哈希表总是比查找表慢。对于查找表,可以使用枚举类型作为“键”,并使用单个间接跳转检索值。这是一个单独的装配操作。 O(1)。哈希表查找首先需要计算哈希值,然后检索值,这样更昂贵。

使用存储函数地址的数组,并使用枚举值访问该数组是好的。但是使用哈希表来做同样的事情会增加一个重要的开销

总结一下,我们有:

  • 费用(Hash_table)>>成本(direct_lookup_table)
  • cost(direct_lookup_table)〜= cost(switch)如果您的编译器将交换机转换为查找表。
  • 费用(转换)>> cost(direct_lookup_table)(O(N)vs O(1))如果你的编译器不转换开关并使用条件,但我想不出任何编译器这样做。
  • 但是内联直接线程会降低代码的可读性。

答案 1 :(得分:20)

Visual Studio 2008附带的STL Map将为每个函数调用提供O(log(n)),因为它隐藏了下面的树结构。 使用现代编译器(取决于实现),一个switch语句将为您提供O(1),编译器将其转换为某种查找表。 所以一般来说,切换速度更快。

然而,请考虑以下事实:

map和switch之间的区别在于:Map可以动态构建,而switch也不能。 Map可以包含任意类型作为键,而switch仅限于c ++ Primitive类型(char,int,enum等)。

顺便说一句,您可以使用哈希映射来实现几乎O(1)调度(但是,根据哈希表的实现,在最坏的情况下有时可能是O(n))。尽管如此,开关仍然会更快。

修改

我写这篇文章的目的只是为了好玩和讨论

我可以为您推荐一个很好的优化,但这取决于您的语言的性质以及您是否可以期待如何使用您的语言。

编写代码时: 您将令牌分为两组,一组经常使用非常高,另一组经常使用低。您还可以对经常使用的高级令牌进行排序。 对于高频率令牌,您可以编写一个if-else系列,其中常用的频率最高。对于经常使用的低频,你要写一个switch语句。

这个想法是使用CPU分支预测,以便甚至避免另一个间接级别(假设if语句中的条件检查几乎是无成本的)。 在大多数情况下,CPU将选择正确的分支而没有任何间接级别。然而,他们将是少数情况下分支将去错误的地方。 根据你的语言的性质,统计它可以提供更好的表现。

编辑:由于下面的一些评论,更改了句子,告诉编译器将始终将切换转换为LUT。

答案 2 :(得分:3)

您对“高效”的定义是什么?如果你的意思更快,那么你可能应该分析一些测试代码以获得明确的答案。如果您正在使用灵活且易于扩展的代码,那么请自己帮忙并使用映射方法。其他一切都只是过早优化......

答案 3 :(得分:2)

就像yossi1981所说,一个开关可以优化为快速查找表,但是不能保证,每个编译器都有其他算法来确定是将连接实现为连续if还是快速查找表,或者可能是组合两者都有。

要获得快速切换,您的值应符合以下规则: 它们应该是连续的,例如0,1,2,3,4。您可以保留一些值,但是0,1,2,34,43之类的东西极不可能被优化。

问题的确如此:在您的应用程序中表现如此重要? 并且,从文件中动态加载其值的地图是否更具可读性和可维护性,而不是跨越多页代码的巨大语句?

答案 4 :(得分:1)

您没有说出您的令牌是什么类型。如果它们不是整数,则没有选择 - 开关仅适用于整数类型。

答案 5 :(得分:0)

C ++标准没有说明其要求的性能,只是功能应该存在。

除非您说明您正在谈论的实施,否则这些关于哪种更好,更快或更有效的问题都是毫无意义的。例如,某个JavaScript实现版本中的字符串处理非常糟糕,但您不能将其推断为相关标准的功能。

我甚至会说无论执行情况如何都没关系,因为switchstd::map提供的功能不同(尽管有重叠)。

在我看来,这种微观优化几乎不需要。