Last active
April 21, 2018 13:29
-
-
Save PhilipRoman/a2f3e1ac63280d1d1793588029919769 to your computer and use it in GitHub Desktop.
Java performance anomaly: the loop with 'beta' method is 2x slower than loop with 'alpha' even though the methods are identical.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.reflect.Field; | |
import java.nio.ByteBuffer; | |
public class Main { | |
private static final int N = 1_000_000; | |
private static Field stringBytes; | |
private static String testString; | |
static { | |
// setup test string: | |
StringBuilder builder = new StringBuilder(1024); | |
for (int i = 0; i < 100; i++) { | |
builder.append("Hello, world!"); | |
} | |
testString = builder.toString(); | |
// setup String byte array field: | |
try { | |
Field stringValue = String.class.getDeclaredField("value"); | |
stringValue.setAccessible(true); | |
stringBytes = stringValue; | |
} catch (NoSuchFieldException e) { | |
e.printStackTrace(); | |
} | |
} | |
public static void main(String[] args) throws IllegalAccessException { | |
ByteBuffer expectedBuffer = ByteBuffer.allocate(2000) | |
.put(testString.getBytes()); | |
ByteBuffer testBuffer = ByteBuffer.allocate(2000); | |
// | |
for (int i = 0; i < 10; i++) { | |
runTests(testBuffer, expectedBuffer); | |
} | |
} | |
private static void runTests(ByteBuffer testBuffer, ByteBuffer expectedBuffer) throws | |
IllegalAccessException { | |
long startTime = System.nanoTime(); | |
for (int i = 0; i < N; i++) { | |
testBuffer.clear(); | |
slow(testBuffer); | |
checkResult(testBuffer, expectedBuffer); | |
} | |
long endTime = System.nanoTime(); | |
System.out.println("Slow: \t" + (endTime - startTime)); | |
// | |
startTime = System.nanoTime(); | |
for (int i = 0; i < N; i++) { | |
testBuffer.clear(); | |
alpha(testBuffer); | |
checkResult(testBuffer, expectedBuffer); | |
} | |
endTime = System.nanoTime(); | |
System.out.println("Alpha: \t" + (endTime - startTime)); | |
// | |
startTime = System.nanoTime(); | |
for (int i = 0; i < N; i++) { | |
testBuffer.clear(); | |
beta(testBuffer); | |
checkResult(testBuffer, expectedBuffer); | |
} | |
endTime = System.nanoTime(); | |
System.out.println("Beta: \t" + (endTime - startTime)); | |
} | |
// make sure the JIT doesn't discard computations | |
private static void checkResult(ByteBuffer got, ByteBuffer expected) { | |
if (!got.equals(expected)) { | |
throw new RuntimeException( | |
String.format("expected: %s, got %s", expected, got) | |
); | |
} | |
} | |
private static void slow(ByteBuffer source) { | |
source.put(testString.getBytes()); | |
} | |
private static void alpha(ByteBuffer source) throws IllegalAccessException { | |
source.put((byte[]) stringBytes.get(testString)); | |
} | |
private static void beta(ByteBuffer source) throws IllegalAccessException { | |
source.put((byte[]) stringBytes.get(testString)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment