跳转表如何工作?

时间:2012-12-21 23:40:35

标签: c++ c performance latency

在以下文件中,第4-5页:

http://www.open-std.org/jtc1/sc22/wg21/docs/ESC_Boston_01_304_paper.pdf

typedef int (* jumpfnct)(void * param); 
static int CaseError(void * param) 
{ 
  return -1; 
} 
static jumpfnct const jumptable[] = 
{ 
  CaseError, CaseError, ... 
  . 
  . 
  . 
  Case44,    CaseError, ... 
  . 
  . 
  . 
  CaseError, Case255 
}; 
  result = index <= 0xFF ? jumptable[index](param) : -1;

它正在比较IF-ELSE和SWITCH,然后介绍这个“跳转表”。显然它是三者中实施最快的。究竟是什么?我看不出它是如何起作用的?

5 个答案:

答案 0 :(得分:3)

跳转表是将一些输入整数映射到动作的方法。它源于您可以使用输入整数作为数组的索引。

代码设置一个指向函数的指针数组。然后使用输入整数来选择这些函数指针。通常,它看起来像是指向函数CaseError的指针。然而,一次又一次,它将被指向一个不同的功能。

它的设计是为了

  jumptable[62] = Case62;
  jumptable[95] = Case95;
  jumptable[35] = Case35;
  jumptable[34] = CaseError; /* For example... and so it goes on */

因此,选择要调用的正确函数是常数时间...使用if-elses和choose,选择正确函数所需的时间取决于输入整数...假设编译器没有优化选择跳转本身...如果它是嵌入式代码,那么这种优化可能已被禁用......你必须检查。

找到正确的函数指针后,最后一行只是调用它:

result = index <= 0xFF ? jumptable[index](param) : -1;

变为

result = index <= 0xFF  /* Check that the index is within
                           the range of the jump table */

         ? jumptable[index](param) /* jumptable[index] selects a function
                                      then it gets called with (param) */

         : -1; /* If the index is out of range, set result to be -1
                  Personally, I think a better choice would be to call
                  CaseError(param) here */

答案 1 :(得分:2)

Jumpfnct是一个指向函数的指针。 Jumptable是一个由多个jumpfncts组成的数组。只需引用它们在数组中的位置即可调用这些函数。

例如,jumptable0将执行第一个函数,传递param。 jumptable1将执行第二个函数等。

如果您不了解函数指针,则不应使用此技巧。它们非常方便,处于狭隘的领域。

当你正在做的是在大量类似的函数调用之间切换时,它非常快速且节省空间。您正在添加一个switch语句不一定具有的函数调用开销,因此它可能并不适合所有情况。如果您的代码是这样的:

switch(x) {
  case 1:
    function1();
    break;
  case 2:
    function2();
    break;
...
}

跳转表可能是一个很好的替代品。但是,如果您的开关是这样的:

switch(x) {
  case 1:
    y++;
    break;
  case 1023:
    y--;
    break;
...
}

这可能不值得做。

我已经在玩具FORTH语言翻译中使用它们,它们非常宝贵,但在大多数情况下,你不会看到使它们值得使用的速度优势。如果它使程序的逻辑更清晰,而不是为了优化,则使用它们。

答案 2 :(得分:1)

此跳转表通过其索引返回指向函数的指针。您可以通过以下方式定义此表:无效索引指向返回某些无效代码的函数(如示例中的-1),并且有效索引指向您需要调用的函数。

构建

jumptable[index]

返回指向函数的指针,此函数被调用

jumptable[index](param)

其中param是一些自定义参数。

答案 3 :(得分:1)

跳转表是一个显而易见但很少使用的优化,由于某种原因似乎已经失宠了。

简单地说,不是测试一个值并退出switch / case或if-else块来分支到函数或代码路径,而是创建一个数组,其中填充了程序可以分支到的函数的地址。

一旦完成,这种安排就消除了if-else和switch / case块测试服务员的无情。该代码使用以其他方式测试的变量if if作为函数指针数组的下标,并直接进行相应的代码 - 如果测试则无任何问题。一个非常有效的分支。汇编代码应该是一个跳跃。

如果您对代码进行了分析,并找到了该计划花费大量时间的热点,那么请考虑采用这种优化来提高性能。如果它是代码热点的一部分,那么这一点可以有很长的路要走。

感谢您的链接。很好找!

答案 4 :(得分:0)

正如上面的评论中所提到的,这个解决方案是否比例如switch语句更有效或更低,取决于每个案例需要完成的工作量。

为要处理的值编写常规switch语句肯定会更清楚地了解代码的作用。因此,除非空间或速度要求要求更复杂的解决方案,否则我建议这不是一个“更好”的解决方案。

然而,

函数指针表是解决某些问题的有效方法。我经常在表中使用函数指针来执行诸如“基准测试11问题的不同解决方案”,其中我有一个结构,其中包含函数和函数的名称,以及一些参数。然后我有一个函数来对代码进行定时和循环几百万次(或者无论如何都需要足够长的测量才能理解)