-
-
Save navyxliu/ee4465e2146ef99c5ae1fa1ba6b70e25 to your computer and use it in GitHub Desktop.
// -Xcomp -Xms16M -Xmx16M -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:-UseOnStackReplacement -XX:CompileOnly='Example2.foo' -XX:CompileCommand=dontinline,Example2.blackhole | |
class Example2 { | |
private Object _cache; | |
public Object foo(boolean cond) { | |
Object x = new Object(); | |
blackhole(); | |
if (cond) { | |
_cache = x; | |
} | |
return x; | |
} | |
public static void blackhole() {} | |
public static void main(String[] args) { | |
Example2 kase = new Example2(); | |
// Epsilon Test: | |
// By setting the maximal heap and use EpsilonGC, let's see how long and how many iterations the program can sustain. | |
// if PEA manages to reduce allocation rate, we expect the program to stay longer. | |
// Roman commented it with a resonable doubt: "or your code slow down the program..." | |
// That's why I suggest to observe iterations. It turns out not trivial because inner OOME will implode hotspot. We don't have a chance to execute the final statement... | |
long iterations = 0; | |
try { | |
while (true) { | |
kase.foo(0 == (iterations & 0xf)); | |
iterations++; | |
} | |
} finally { | |
System.err.println("Epsilon Test: " + iterations); | |
} | |
} | |
} |
I wonder how can you deal with the identity issue mentioned by Vladimir, i.e this code
public void foo(boolean cond1, boolean cond2) {
Object x = new Object();
blackhole();
if (cond1) {
_cache1 = x;
}
blackhole();
if (cond2) {
_cache2 = x;
}
}
Here if cond1 && cond2
, we must have _cache1 == _cache2
, so you can't perform the transformation as you do above. In other word, the transformation can only be done if the object is sure to have not escaped at that point.
C2 generates code like this after parser with -XX:+DoPartialEscapeAnalysis
.
public void foo(boolean cond1, boolean cond2) {
Object x0 = new Object();
blackhole();
if (cond1) {
x1 = new Object();
_cache1 = x1;
}
x2 = phi(x2=new Object(), x1);
blackhole();
if (cond2) {
_cache2 = x2;
}
}
if cond1 == true && cond2== true, then we have _cache1 == __cache2 == x1
Thank you for vetting this. here is a modified program from and enable assertion.
We can verify that cache1 == cache2.
The reason is explained above. One thing is worth noting: when we parse "if(cond2) ...", object x has been materialized. PEA won't materialize it again at "_cache2 = x".
// -ea -Xcomp -Xms16M -Xmx16M -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:-UseOnStackReplacement -XX:CompileOnly='Example2_merykitty.foo' -XX:CompileCommand=dontinline,Example2_merykitty.blackhole -XX:+DoPartialEscapeAnalysis
class Example2_merykitty {
private static Object _cache1;
private static Object _cache2;
public void foo(boolean cond1, boolean cond2) {
Object x = new Object();
blackhole();
if (cond1) {
_cache1 = x;
}
blackhole();
if (cond2) {
_cache2 = x;
}
}
public static void blackhole() {}
public static void main(String[] args) {
Example2_merykitty kase = new Example2_merykitty();
long iterations = 0;
try {
while (true) {
boolean cond = 0 == (iterations & 0xf);
kase.foo(cond, cond);
assert Example2_merykitty._cache1 == Example2_merykitty._cache2 :"check";
iterations++;
}
} finally {
System.err.println("Epsilon Test: " + iterations);
}
}
}
Link to Example3_1. It features non-trivial object with stateful fields and inlined methods.
https://gist.github.com/navyxliu/74d0546004a773cb5219754f6ed63d43
We expect to transform code to this after PEA. PHI node merges 2 predecessor basic blocks.
x2 is placed because x1 has been materialized. x0 becomes an obsolete object after then. C2 EA/SR will take care of it.