Problem: I have a method that compiles to over 8000 bytes of Java bytecode. HotSpot has a magic limit that makes the JIT not kick in for methods that exceed 8000 bytes. (Yes, it is reasonable to have a huge method. This is a tokenizer loop.) The method is in a library and I don't want to require users of the library to have to configure HotSpot to deactivate the magic limit.
问题:我有一个编译超过8000字节的Java字节码的方法。 HotSpot有一个神奇的限制,使得JIT不会超过8000字节的方法。 (是的,有一个庞大的方法是合理的。这是一个标记器循环。)该方法在库中,我不想要求库的用户必须配置HotSpot来停用魔术限制。
Observation: Decompiling the bytecode shows that Eclipse Java Compiler generates a lot of pointless gotos. (javac is even worse.) That is, there are gotos that are only reachable from jumps. Obviously, the jump that jumps to the goto should instead jump directly where the goto jumps and the goto should be eliminated.
观察:反编译字节码表明Eclipse Java Compiler生成了许多无意义的getos。 (javac更糟糕。)也就是说,有些只能从跳跃中获得。显然,跳转到goto的跳转应该直接跳转到goto跳转的位置,goto应该被消除。
Question: Is there a bytecode optimizer for Java 5 class files that flattens pointless jump chains and then removes unnecessary gotos?
问题:是否有针对Java 5类文件的字节码优化器,可以展平无意义的跳转链,然后删除不必要的getos?
Edit: I mean patterns like:
编辑:我的意思是:
8698: goto 8548
8701: goto 0
Obviously, the second goto can only be reached by a jump to 8701 which might as well be a direct jump to 0.
显然,第二个goto只能通过跳转到8701到达,这可能也是直接跳转到0。
On a second investigation, this questionable pattern is more common:
在第二次调查中,这种可疑模式更为常见:
4257: if_icmpne 4263
4260: goto 8704
4263: aload_0
Where obviously, one would like the compiler to reverse the "not equal" comparison to "equal" comparison, jump to 8704 and eliminate the goto.
显然,人们希望编译器将“不等于”比较反转为“相等”比较,跳转到8704并消除goto。
7 个解决方案
#1
I feel your pain. I had to write a parser once that had around 5kloc of if(str.equals(...)) code. I broke into several methods along the lines of parse1, parse2, etc. If parse1 didn't result in a parsed answer, parse2 was called, etc. This isn't necessarily best-practices, but it does do what you need it to.
我感觉到你的痛苦。我必须编写一个解析器,它有大约5kloc的if(str.equals(...))代码。我按照parse1,parse2等的方式划分了几个方法。如果parse1没有得到解析的答案,则调用parse2等。这不一定是最佳实践,但它确实做了你需要的。
#2
Does it make a difference if you don't compile with debug symbols (i.e. the -g flag in javac)? That might bring the method down below the magic limit.
如果不使用调试符号(即javac中的-g标志)进行编译,是否会有所不同?这可能会使方法低于魔法限制。
#3
Would it be impossible to refactor the method into submethods? Modern JIT's inline those calls anyway.
是不可能将该方法重构为子方法?无论如何,现代JIT都会内联这些电话。
#4
One method compiling to over 8000 bytes? Does anybody understand that code? Is is testable? Try to split it up into multiple (private?) methods with meaningfull names instead of hassling with the optimizer!
一种编译超过8000字节的方法?有人理解这个代码吗?是可以测试的吗?尝试将其拆分为多个(私有?)方法,使用有意义的名称,而不是与优化器争吵!
OK, maybe there are cases legitimate large methods. But sorry, there are no hints in the question.
好吧,也许有些合法的大型方法。但对不起,这个问题没有任何暗示。
#5
If it's a tokenizer loop, would it be better to do it with a data drivven set of mappings and a bit of reflection as appropriate?
如果它是一个标记化器循环,那么使用数据驱动的映射集和适当的一些反射来做它会更好吗?
So you would store your token matches in a structure that maps them to data about that token's syntax and methods implementing associated functions. Lookup can be optimised over the structure and you avoid the big loop.
因此,您可以将令牌匹配存储在一个结构中,该结构将它们映射到有关该令牌的语法和实现相关函数的方法的数据。查找可以在结构上进行优化,避免大循环。
That introduces the issue of keeping the data and implementation in sync, but you could generate the data from your codebase with a doclet or possibly annotation.
这引入了保持数据和实现保持同步的问题,但您可以使用doclet或可能的注释从代码库生成数据。
Without knowing exactly what your big method does, we're limited to trying to optimise it the way that you're assuming is best (and which is apparently not possible anyway).
如果不知道你的大方法究竟做了什么,我们只能尝试以你认为最好的方式进行优化(而且无论如何这显然是不可能的)。
#6
does your performance increase if you run a bytecode shrinker/obfuscator on your class? e.g., yguard, proguard, ...
如果在类上运行字节码收缩器/混淆器,您的性能会提高吗?例如,yguard,proguard,......
maybe you can write a class file postprocessor using asm because your use case is so specific.
也许你可以使用asm编写一个类文件后处理器,因为你的用例非常具体。
even if you remove all pointless gotos, does that bring you under the magic limit?
即使你删除所有无意义的冒险,这是否会让你处于魔力限制之下?
#7
A list of bytecode libraries mentions BCEL and ASM, that I'd heard of before, along with many others doing various things.
一个字节码库列表提到了我以前听过的BCEL和ASM,以及许多其他人在做各种事情。
#1
I feel your pain. I had to write a parser once that had around 5kloc of if(str.equals(...)) code. I broke into several methods along the lines of parse1, parse2, etc. If parse1 didn't result in a parsed answer, parse2 was called, etc. This isn't necessarily best-practices, but it does do what you need it to.
我感觉到你的痛苦。我必须编写一个解析器,它有大约5kloc的if(str.equals(...))代码。我按照parse1,parse2等的方式划分了几个方法。如果parse1没有得到解析的答案,则调用parse2等。这不一定是最佳实践,但它确实做了你需要的。
#2
Does it make a difference if you don't compile with debug symbols (i.e. the -g flag in javac)? That might bring the method down below the magic limit.
如果不使用调试符号(即javac中的-g标志)进行编译,是否会有所不同?这可能会使方法低于魔法限制。
#3
Would it be impossible to refactor the method into submethods? Modern JIT's inline those calls anyway.
是不可能将该方法重构为子方法?无论如何,现代JIT都会内联这些电话。
#4
One method compiling to over 8000 bytes? Does anybody understand that code? Is is testable? Try to split it up into multiple (private?) methods with meaningfull names instead of hassling with the optimizer!
一种编译超过8000字节的方法?有人理解这个代码吗?是可以测试的吗?尝试将其拆分为多个(私有?)方法,使用有意义的名称,而不是与优化器争吵!
OK, maybe there are cases legitimate large methods. But sorry, there are no hints in the question.
好吧,也许有些合法的大型方法。但对不起,这个问题没有任何暗示。
#5
If it's a tokenizer loop, would it be better to do it with a data drivven set of mappings and a bit of reflection as appropriate?
如果它是一个标记化器循环,那么使用数据驱动的映射集和适当的一些反射来做它会更好吗?
So you would store your token matches in a structure that maps them to data about that token's syntax and methods implementing associated functions. Lookup can be optimised over the structure and you avoid the big loop.
因此,您可以将令牌匹配存储在一个结构中,该结构将它们映射到有关该令牌的语法和实现相关函数的方法的数据。查找可以在结构上进行优化,避免大循环。
That introduces the issue of keeping the data and implementation in sync, but you could generate the data from your codebase with a doclet or possibly annotation.
这引入了保持数据和实现保持同步的问题,但您可以使用doclet或可能的注释从代码库生成数据。
Without knowing exactly what your big method does, we're limited to trying to optimise it the way that you're assuming is best (and which is apparently not possible anyway).
如果不知道你的大方法究竟做了什么,我们只能尝试以你认为最好的方式进行优化(而且无论如何这显然是不可能的)。
#6
does your performance increase if you run a bytecode shrinker/obfuscator on your class? e.g., yguard, proguard, ...
如果在类上运行字节码收缩器/混淆器,您的性能会提高吗?例如,yguard,proguard,......
maybe you can write a class file postprocessor using asm because your use case is so specific.
也许你可以使用asm编写一个类文件后处理器,因为你的用例非常具体。
even if you remove all pointless gotos, does that bring you under the magic limit?
即使你删除所有无意义的冒险,这是否会让你处于魔力限制之下?
#7
A list of bytecode libraries mentions BCEL and ASM, that I'd heard of before, along with many others doing various things.
一个字节码库列表提到了我以前听过的BCEL和ASM,以及许多其他人在做各种事情。