简体   繁体   English

临时变量的范围 - Eclipse /编译器是否优化?

[英]Scope of temporary variables — does Eclipse/compiler optimize?

First of all, I ask those with "premature optimization" phobia to spare me: I don't want to optimize anything, I'm just curious . 首先,我要求那些“过早优化”恐惧症的人免除我:我不想优化任何东西,我只是好奇

I read/observed two things, including on stackoverflow (can't find the link now): 我阅读/观察了两件事,包括在stackoverflow上(现在找不到链接):

  • For method calls, memory for all local variables is reserved "at the beginning" of the method, ie even for the variables that are declared in lower-level scopes (I know this is poor wording scientifically, eg ignoring how calling mechanism works etc., but I hope the point is clear). 对于方法调用, 所有局部变量的内存保留在方法的“开头”,即使是在较低级别范围内声明的变量(我知道这是科学上不好的措辞,例如忽略调用机制如何工作等等)。 ,但我希望这一点很清楚)。 Obviously, scopes don't exist for the running program, they are only on source code level for better readability, maintaibility, code structuring, telling the compiler about our intentions (eg giving optimization hints, see below). 显然,运行程序不存在范围,它们仅在源代码级别上,以提高可读性,维护性,代码结构,告诉编译器我们的意图(例如,给出优化提示,见下文)。
  • One of advantages of declaring variables in the smallest possible scope (ie the highest level where they're still needed) is that the "compiler can reuse the memory for other temporary variables (in other blocks)". 在尽可能小的范围内声明变量(即仍然需要它们的最高级别)的一个优点是“编译器可以将内存重用于其他临时变量(在其他块中)”。 This sounds clear & logical to me. 这对我来说听起来很清楚。

I'm wondering what the compiler/JIT/whatever can actually optimize, and what it can't. 我想知道编译器/ JIT /什么可以实际优化,什么不能。

Here is the following method (assume that the variables are actually used , so they can't be optimized away for that reason): 以下是以下方法(假设实际使用了变量,因此无法对其进行优化):

// The method does many (useful) things, but these were cut here
public void myMethod() {
    int var1 = 1;
    ... // do work
    if (something) {
        int var2 = 2;
        int var3 = 3;
        ... // do work
    }
    int var4 = 4;
    int var5 = 5;
    ... // do work
}

1.) Is the compiler able to detect that the space for var2 and var3 can be reused for var4 and var5 ? 1.)编译器是否能够检测到var2var3的空间可以重用于var4var5 I don't remember seeing such a thing in disassembled bytecodes. 我不记得在反汇编的字节码中看到过这样的事情。

2.) Is the code method above equivalent to the case when the end of the method is put to {} too? 2.)上面的代码方法是否等同于将方法的结尾放到{}的情况?

public void myMethod() {
    int var1 = 1;
    ... // do work
    if (something) {
        int var2 = 2;
        int var3 = 3;
        ... // do work
    }
    {
        int var4 = 4;
        int var5 = 5;
        ... // do work
    }
}

Finally, let's see a simpler case: 最后,让我们看一个更简单的案例:

public void myMethod() {
    int var1 = 1;
    ... // do work, and then don't refer to var1 any more

    int var4 = 4;
    int var5 = 5;
    ... // do work      
}

3.) In this case, can var1 memory be reused for var4 (or var5 )? 3.)在这种情况下,可以为var4 (或var5 )重用var1内存吗? Ie in such a case, it is enough for the method to have memory for two local int variables, not for three. 即在这种情况下,该方法足以为两个本地int变量提供内存,而不是三个。

(I know that in theory, these are obvious cases for the compiler, but sometimes I overlook things eg why a compiler can't do or assume anything.) (我知道理论上,这些是编译器的明显案例,但有时我会忽略一些事情,例如为什么编译器不能做或假设任何事情。)

I can speak for compilers for real CPUs, I'm not sure about JVM compiler and I don't think that at compile level code is optimized as much as you think (the Java platform simply don't care too much about memory foorprint as you can imagine). 我可以代表真正的CPU编译器,我不确定JVM编译器,我不认为在编译级别代码的优化程度与你想象的一样(Java平台根本不关心内存foorprint如何你可以想象)。

For real compilers these scenarios are actually optimized very often. 对于真正的编译器,这些场景实际上经常被优化。 This is done not at high level language level but at a lower intermediate level like at RTL level. 这不是在高级语言级别,而是在较低的中级水平,如RTL级别。 Everything is made with the purpose of register allocation or stack allocation and works by computing living sets for variables inside functions. 一切都是为了寄存器分配或堆栈分配的目的,并通过计算函数内部变量的生存集来工作。

This means that when the code is compiled everything is translated to RTL by assuming an arbitrary number of temporary registers and then for each temporary, its live state is calculated through the so-called live variable analysis . 这意味着当编译代码时,通过假定任意数量的临时寄存器将所有内容转换为RTL,然后对于每个临时寄存器,通过所谓的实时变量分析来计算其实时状态。 This is just one of the ways to optimize such things. 这只是优化此类事物的方法之一。

Eg 例如

  • var1 live scope is from instruction 1 to 10 var1实时范围是从指令1到10
  • var2 live scope is from instruction 5 to 17 var2 live scope来自指令5到17
  • var3 live scope is from instruction 3 to 25 var3 live scope来自指令3到25
  • and so on 等等

This is done after having splitted the code into blocks which does not contain jumps or labels so that you are sure about the flow inside any specified block. 这是在将代码拆分为不包含跳转或标签的块之后完成的,这样您就可以确定任何指定块内的流。

After this computation you can easily see which variables are needed for most time, which become useless and can free their reserved space and so on. 在这个计算之后,您可以很容易地看到大多数时间需要哪些变量,这些变量变得无用并且可以释放它们的预留空间等等。 This is done so that you are able to fit as much variables as possible into registers and optimize the assembled code a lot. 这样做是为了使您能够将尽可能多的变量放入寄存器并大量优化汇编代码。

Personally I don't think the javac does any of this (even because the JVM is stack based so this would just break memory allocation on objects without any need nowadays), but my is just speculation. 我个人认为javac没有做到这一点(即使因为JVM是基于堆栈的,所以这只会破坏对象上的内存分配而现在没有任何需要),但我只是猜测。

  1. Yes. 是。 The stack slot can be reused after the nested }, and I have seen javac do so. 堆栈插槽可以在嵌套}之后重用,我看到javac这样做了。

  2. Yes. 是。 Cases 1 and 2 are equivalent. 案例1和案例2相同。 The second nested {} changes nothing, unless something follows it. 第二个嵌套{}没有任何改变,除非它跟随它。

  3. The Java compiler won't optimize this, although in theory given all the right conditions it conceivably could. Java编译器不会对此进行优化,尽管在理论上给出了它可以想到的所有正确条件。 If the variables are final it is possible not to assign a slot for them at all. 如果变量是最终的,则可能根本不为它们分配槽。

Another thing you should note is that there is no byte code instruction corresponding to the nested }, so the JVM and therefore HotSpot has no basis for knowing where a nested scope ends and therefore where a stack slot changes usage. 您应该注意的另一件事是没有与嵌套}相对应的字节代码指令,因此JVM和HotSpot没有基础知道嵌套作用域的结束位置,因此堆栈插槽更改使用的位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM