Difference between JVM's LookupSwitch and TableSwitch?
我很难理解Java字节码中的LoopUpwitter和TabLeSwitCH。
如果我理解得很好,LoopUpTwitter和TabLaswitCH都对应于Java源的EDCOX1×0语句吗?为什么一个Java语句生成2个不同的字节码?
Jasmin文件:
- 查找开关
- 桌上女巫
区别在于lookupswitch使用带有键和标签的表,而tableswitch只使用带有标签的表。
在执行tableswitch时,堆栈顶部的int值直接用作表中的索引,以获取跳转目标并立即执行跳转。整个lookup+jump过程是一个O(1)操作,这意味着它的速度非常快。
执行lookupswitch时,将堆栈顶部的int值与表中的键进行比较,直到找到匹配项,然后使用该键旁边的跳转目标执行跳转。由于lookupswitch表总是必须进行排序,因此对于每个x 编译器根据switch语句的紧凑程度来决定要使用哪条指令,例如:
1 2 3 4 5 6 | switch (inputValue) { case 1: // ... case 2: // ... case 3: // ... default: // ... } |
上面的开关非常紧凑,没有数字"孔"。编译器将创建这样的表开关:
1 2 3 4 5 | tableswitch 1 3 OneLabel TwoLabel ThreeLabel default: DefaultLabel |
Jasmin页面的伪代码很好地解释了这一点:
1 2 3 4 5 6 | int val = pop(); // pop an int from the stack if (val < low || val > high) { // if its less than <low> or greater than <high>, pc += default; // branch to default } else { // otherwise pc += table[val - low]; // branch to entry in table } |
这段代码非常清楚这种TableSwitch是如何工作的。
即使有一些孔,开关也可以很紧凑,例如
1 2 3 4 5 6 7 | switch (inputValue) { case 1: // ... case 3: // ... case 4: // ... case 5: // ... default: // ... } |
上面的开关"几乎紧凑",只有一个孔。编译器可以生成以下指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 | tableswitch 1 6 OneLabel FakeTwoLabel ThreeLabel FourLabel FiveLabel default: DefaultLabel ; <...code left out...> FakeTwoLabel: DefaultLabel: ; default code |
如您所见,编译器必须为2添加一个假的case,
因此,对于编译器创建TableSwitch来说,开关并不一定要非常紧凑,但它至少应该非常接近紧凑性。现在考虑以下开关:
1 2 3 4 5 6 7 | switch (inputValue) { case 1: // ... case 10: // ... case 100: // ... case 1000: // ... default: // ... } |
这个开关几乎没有紧凑性,它有超过百倍的洞比值多。我们可以称之为备用开关。编译器将不得不生成近千个伪案例来将此开关表示为TableSwitch。结果将是一个巨大的表,极大地放大了类文件的大小。这是不实际的。相反,它将生成一个lookupswitch:
1 2 3 4 5 6 | lookupswitch 1 : Label1 10 : Label10 100 : Label100 1000 : Label1000 default : DefaultLabel |
这个表只有5个条目,而不是1000多个条目。该表有4个实数,o(log 4)是2(这里的log 2是以btw为基,而不是以10为基,因为计算机是用二进制数操作的)。这意味着,VM最多需要两个比较来查找输入值的标签或得出结论,即该值不在表中,因此必须执行默认值。即使该表有100个条目,也最多需要7个比较,才能找到正确的标签或决定跳到默认标签(7个比较远小于100个比较,您不认为吗?).
所以,这两个指令是可以互换的,或者两个指令的原因有历史原因,这是无稽之谈。对于两种不同的情况,有两种指令,一种用于具有紧凑型值(最大速度)的开关,另一种用于具有备用值(不是最大速度,但仍然具有良好的速度和非常紧凑的表表示,不管所有数字孔如何)。
当正确的Javac 1.8.0 45 Compile开关到Either One?
决定何时使用,您可以使用
我们知道
然后我们抓住
1 | hg grep -i tableswitch |
第一个结果是Langtools/SRC/share/classes/com/sun/tools/javac/jvm/gen.java:
ZZU1
哪里
- 最大值
- 最小案例价值
因此,我们得出结论认为,这既考虑到时间的复杂性,也考虑到空间的复杂性,同时考虑到时间的复杂性,加权为3。
自从有了
奖金:C++++++执行还可以在O(1)Jump Table和O(Long(n))二进制搜索之间进行模拟选择:在if-else Statement上的开关优势
Java虚拟机规范描述了差异。当开关的事例可以有效地表示为目标偏移表中的索引时,使用table switch指令。"该规范描述了更多详细信息。
由于Java字节码与下划线的机器代码(例如Sun自己的CPU)的某种特定绑定,所以我怀疑它大部分是历史的。
tableswitch本质上是一个计算跳转,其中目的地是从查找表中获取的。相反,lookupswitch需要对每个值进行比较,基本上是通过表元素进行迭代,直到找到匹配的值。
显然,这些操作码是可互换的,但基于值,其中一个或另一个操作码可以更快或更紧凑(例如,比较键组与键组之间有较大间隙的键组)。