I noticed that there may be another optimisation that can be added to IntegerEqualsNode. Specifically, comparing the And and Or of two values is equivalent to comparing the values directly.
This optimisation has also been formally verified in an interactive theorem prover (Isabelle/HOL).
Sample Program
I've written a sample program to demonstrate the speed-up of this optimisation:
public class BinaryExprTest {
public static void main(String[] args) {
long start = System.nanoTime();
long sum = 0;
for (int i = 1; i < 1000000; i++) {
sum += calculate(i);
}
System.out.println(sum);
long end = System.nanoTime();
System.out.println("Time taken: " + ((end - start) / 1000000) + "ms");
}
static int calculate(int i) {
int m = 1;
int s = i;
boolean condition = false;
for (int j = 1; j < 1000; j++) {
s = s * 6 + j;
m = calc2(s,j);
//condition = ((m & s) == (m | s)); // Unoptimised
condition = (m == s); // Optimised
}
return (condition) ? 1 : 2;
}
static int calc2(int i, int j) {
int x = i;
for (int y = 0; y < 10; y++) {
x = x + ((j + (i*10)) + y);
}
return x;
}
}
The optimised version is around 15-20% faster than the unoptimised version, yielding runtimes of approximately 3.7s and 4.5s, respectively.
Unit Test
I've also written a JUnit test to check whether this optimisation is applied:
package jdk.graal.compiler.core.test;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.calc.AndNode;
import jdk.graal.compiler.nodes.calc.IntegerEqualsNode;
import jdk.graal.compiler.nodes.calc.OrNode;
import org.junit.Test;
public class NewOptimizationTest extends GraalCompilerTest {
// ((x & y) == (x | y)) |-> (x == y)
public static boolean andOrEquals1(int x, int y) {
return ((x & y) == (x | y));
}
public static boolean andOrEquals2(int x, int y) {
return ((x & y) == (y | x));
}
public static boolean andOrEquals3(int x, int y) {
return ((x | y) == (x & y));
}
public static boolean andOrEquals4(int x, int y) {
return ((x | y) == (y & x));
}
private void checkNodes(String methodName) {
StructuredGraph graph = parseForCompile(getResolvedJavaMethod(methodName));
System.out.println("(Before) Nodes in graph: ");
System.out.println("IntegerEquals: " + graph.getNodes().filter(IntegerEqualsNode.class).count());
System.out.println("And: " + graph.getNodes().filter(AndNode.class).count());
System.out.println("Or: " + graph.getNodes().filter(OrNode.class).count());
createCanonicalizerPhase().apply(graph, getProviders());
System.out.println("(After) Nodes in graph: ");
System.out.println("IntegerEquals: " + graph.getNodes().filter(IntegerEqualsNode.class).count());
System.out.println("And: " + graph.getNodes().filter(AndNode.class).count());
System.out.println("Or: " + graph.getNodes().filter(OrNode.class).count());
// If there are no AndNodes or OrNodes in the graph, the optimisation was applied
assertTrue(graph.getNodes().filter(AndNode.class).count() == 0);
assertTrue(graph.getNodes().filter(OrNode.class).count() == 0);
}
@Test
public void testOptimization1() {
checkNodes("andOrEquals1");
}
@Test
public void testOptimization2() {
checkNodes("andOrEquals2");
}
@Test
public void testOptimization3() {
checkNodes("andOrEquals3");
}
@Test
public void testOptimization4() {
checkNodes("andOrEquals4");
}
}
Running this test produces the following output:
MxJUnitCore
JUnit version 4.12
.(Before) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
(After) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
E
testOptimization1(jdk.graal.compiler.core.test.NewOptimizationTest)
java.lang.AssertionError
...
.(Before) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
(After) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
E
testOptimization2(jdk.graal.compiler.core.test.NewOptimizationTest)
java.lang.AssertionError
...
.(Before) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
(After) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
E
testOptimization3(jdk.graal.compiler.core.test.NewOptimizationTest)
java.lang.AssertionError
...
.(Before) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
(After) Nodes in graph:
IntegerEquals: 1
And: 1
Or: 1
E
testOptimization4(jdk.graal.compiler.core.test.NewOptimizationTest)
java.lang.AssertionError
...
Time: 0.79
There were 4 failures:
1) jdk.graal.compiler.core.test.NewOptimizationTest#testOptimization1
java.lang.AssertionError
...
2) jdk.graal.compiler.core.test.NewOptimizationTest#testOptimization2
java.lang.AssertionError
...
3) jdk.graal.compiler.core.test.NewOptimizationTest#testOptimization3
java.lang.AssertionError
...
4) jdk.graal.compiler.core.test.NewOptimizationTest#testOptimization4
java.lang.AssertionError
...
FAILURES!!!
Tests run: 4, Failures: 4
...
Overview
Hi,
I noticed that there may be another optimisation that can be added to IntegerEqualsNode. Specifically, comparing the And and Or of two values is equivalent to comparing the values directly.
This optimisation has also been formally verified in an interactive theorem prover (Isabelle/HOL).
Sample Program
I've written a sample program to demonstrate the speed-up of this optimisation:
The optimised version is around 15-20% faster than the unoptimised version, yielding runtimes of approximately 3.7s and 4.5s, respectively.
Unit Test
I've also written a JUnit test to check whether this optimisation is applied:
Running this test produces the following output: