Last active
December 21, 2021 16:59
-
-
Save amirhadadi/9505c3f5d9ad68cad2fbfd1b9e01f0b8 to your computer and use it in GitHub Desktop.
String benchmark comparing safe and unsafe accesses
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
package org.sample; | |
import org.openjdk.jmh.annotations.Benchmark; | |
import org.openjdk.jmh.annotations.BenchmarkMode; | |
import org.openjdk.jmh.annotations.Fork; | |
import org.openjdk.jmh.annotations.Mode; | |
import org.openjdk.jmh.annotations.OutputTimeUnit; | |
import sun.misc.Unsafe; | |
import java.lang.reflect.Field; | |
import java.nio.charset.StandardCharsets; | |
import java.security.AccessController; | |
import java.security.PrivilegedExceptionAction; | |
import java.util.concurrent.TimeUnit; | |
@Fork(4) | |
public class StringBenchmark { | |
private static final Unsafe unsafe = getUnsafe(); | |
private static final byte[] german = "Quizdeltagerne spiste jordb\u00e6r med fl\u00f8de, mens cirkusklovnen".getBytes(StandardCharsets.UTF_8); | |
@Benchmark | |
@BenchmarkMode(Mode.AverageTime) | |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | |
public byte[] safeDecoding() { | |
return safeDecode(german, 0, german.length); | |
} | |
@Benchmark | |
@BenchmarkMode(Mode.AverageTime) | |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | |
public byte[] unsafeDecoding() { | |
return unsafeDecode(german, 0, german.length); | |
} | |
private static byte[] safeDecode(byte[] bytes, int offset, int length) { | |
checkBoundsOffCount(offset, length, bytes.length); | |
int sl = offset + length; | |
int dp = 0; | |
byte[] dst = new byte[length]; | |
while (offset < sl) { | |
int b1 = bytes[offset]; | |
if (b1 >= 0) { | |
dst[dp++] = (byte)b1; | |
offset++; | |
continue; | |
} | |
if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) && | |
offset + 1 < sl) { | |
int b2 = bytes[offset + 1]; | |
if (!isNotContinuation(b2)) { | |
dst[dp++] = (byte)decode2(b1, b2); | |
offset += 2; | |
continue; | |
} | |
} | |
break; | |
} | |
return dst; | |
} | |
private static byte[] unsafeDecode(byte[] bytes, int offset, int length) { | |
checkBoundsOffCount(offset, length, bytes.length); | |
int sl = offset + length; | |
int dp = 0; | |
byte[] dst = new byte[length]; | |
while (offset < sl) { | |
int b1 = unsafe.getByte(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET + offset * Unsafe.ARRAY_BYTE_INDEX_SCALE); | |
if (b1 >= 0) { | |
dst[dp++] = (byte)b1; | |
offset++; | |
continue; | |
} | |
if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) && | |
offset + 1 < sl) { | |
int b2 = unsafe.getByte(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset + 1) * Unsafe.ARRAY_BYTE_INDEX_SCALE); | |
if (!isNotContinuation(b2)) { | |
dst[dp++] = (byte)decode2(b1, b2); | |
offset += 2; | |
continue; | |
} | |
} | |
break; | |
} | |
return dst; | |
} | |
private static boolean isNotContinuation(int b) { | |
return (b & 0xc0) != 0x80; | |
} | |
private static char decode2(int b1, int b2) { | |
return (char)(((b1 << 6) ^ b2) ^ | |
(((byte) 0xC0 << 6) ^ | |
((byte) 0x80 << 0))); | |
} | |
static void checkBoundsOffCount(int offset, int count, int length) { | |
if (offset < 0 || count < 0 || offset > length - count) { | |
throw new StringIndexOutOfBoundsException( | |
"offset " + offset + ", count " + count + ", length " + length); | |
} | |
} | |
/** | |
* Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform. | |
*/ | |
static Unsafe getUnsafe() { | |
Unsafe unsafe = null; | |
try { | |
unsafe = | |
AccessController.doPrivileged( | |
new PrivilegedExceptionAction<Unsafe>() { | |
@Override | |
public Unsafe run() throws Exception { | |
Class<Unsafe> k = Unsafe.class; | |
for (Field f : k.getDeclaredFields()) { | |
f.setAccessible(true); | |
Object x = f.get(null); | |
if (k.isInstance(x)) { | |
return k.cast(x); | |
} | |
} | |
// The sun.misc.Unsafe field does not exist. | |
return null; | |
} | |
}); | |
} catch (Throwable e) { | |
// Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError | |
// for Unsafe. | |
} | |
return unsafe; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment