8284880: Re-examine sun.invoke.util.Wrapper hash tables

Reviewed-by: erikj, mchung
This commit is contained in:
Claes Redestad 2022-04-19 20:34:45 +00:00
parent e307bc8694
commit 5df8bd6b4e
3 changed files with 200 additions and 101 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -94,7 +94,8 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \
DISABLED_WARNINGS := processing rawtypes cast serial, \
SRC := $(MICROBENCHMARK_SRC), \
BIN := $(MICROBENCHMARK_CLASSES), \
JAVAC_FLAGS := --add-exports java.base/sun.security.util=ALL-UNNAMED, \
JAVAC_FLAGS := --add-exports java.base/sun.security.util=ALL-UNNAMED \
--add-exports java.base/sun.invoke.util=ALL-UNNAMED, \
JAVA_FLAGS := --add-modules jdk.unsupported --limit-modules java.management, \
))

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,8 @@
package sun.invoke.util;
import jdk.internal.vm.annotation.DontInline;
public enum Wrapper {
// wrapperType simple primitiveType simple char emptyArray format
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1)),
@ -229,28 +231,17 @@ public enum Wrapper {
* instead of no value at all.)
*/
public Object zero() {
switch (this) {
case BOOLEAN:
return Boolean.FALSE;
case INT:
return (Integer)0;
case BYTE:
return (Byte)(byte)0;
case CHAR:
return (Character)(char)0;
case SHORT:
return (Short)(short)0;
case LONG:
return (Long)(long)0;
case FLOAT:
return FLOAT_ZERO;
case DOUBLE:
return DOUBLE_ZERO;
case VOID:
case OBJECT:
default:
return null;
}
return switch (this) {
case BOOLEAN -> Boolean.FALSE;
case INT -> (Integer)0;
case BYTE -> (Byte)(byte)0;
case CHAR -> (Character)(char)0;
case SHORT -> (Short)(short)0;
case LONG -> (Long)(long)0;
case FLOAT -> FLOAT_ZERO;
case DOUBLE -> DOUBLE_ZERO;
default -> null;
};
}
private static final Object DOUBLE_ZERO = (Double)(double)0;
@ -268,11 +259,16 @@ public enum Wrapper {
* @throws IllegalArgumentException for unexpected types
*/
public static Wrapper forPrimitiveType(Class<?> type) {
Wrapper w = findPrimitiveType(type);
if (w != null) return w;
if (type.isPrimitive())
throw new InternalError(); // redo hash function
throw newIllegalArgumentException("not primitive: "+type);
if (type == int.class) return INT;
if (type == long.class) return LONG;
if (type == boolean.class) return BOOLEAN;
if (type == short.class) return SHORT;
if (type == byte.class) return BYTE;
if (type == char.class) return CHAR;
if (type == float.class) return FLOAT;
if (type == double.class) return DOUBLE;
if (type == void.class) return VOID;
throw newIllegalArgumentException("not primitive: " + type);
}
/** Return the wrapper that corresponds to the provided basic type char.
@ -280,26 +276,14 @@ public enum Wrapper {
* @throws IllegalArgumentException for unexpected types
*/
public static Wrapper forPrimitiveType(char basicTypeChar) {
switch (basicTypeChar) {
case 'I': return INT;
case 'J': return LONG;
case 'S': return SHORT;
case 'B': return BYTE;
case 'C': return CHAR;
case 'F': return FLOAT;
case 'D': return DOUBLE;
case 'Z': return BOOLEAN;
case 'V': return VOID;
default: throw newIllegalArgumentException("not primitive: " + basicTypeChar);
Wrapper w = FROM_CHAR[(basicTypeChar + (basicTypeChar >> 1)) & 0xf];
if (w == null || w.basicTypeChar != basicTypeChar) {
throw basicTypeError(basicTypeChar);
}
}
static Wrapper findPrimitiveType(Class<?> type) {
Wrapper w = FROM_PRIM[hashPrim(type)];
if (w != null && w.primitiveType == type) {
return w;
if (w == OBJECT) {
throw newIllegalArgumentException("not primitive: " + basicTypeChar);
}
return null;
return w;
}
/** Return the wrapper that wraps values into the given wrapper type.
@ -310,81 +294,80 @@ public enum Wrapper {
*/
public static Wrapper forWrapperType(Class<?> type) {
Wrapper w = findWrapperType(type);
if (w != null) return w;
for (Wrapper x : values())
if (x.wrapperType == type)
throw new InternalError(); // redo hash function
throw newIllegalArgumentException("not wrapper: "+type);
if (w != null) {
return w;
}
throw wrapperTypeError(type);
}
static Wrapper findWrapperType(Class<?> type) {
Wrapper w = FROM_WRAP[hashWrap(type)];
if (w != null && w.wrapperType == type) {
return w;
}
if (type == Object.class) return OBJECT;
if (type == Integer.class) return INT;
if (type == Long.class) return LONG;
if (type == Boolean.class) return BOOLEAN;
if (type == Short.class) return SHORT;
if (type == Byte.class) return BYTE;
if (type == Character.class) return CHAR;
if (type == Float.class) return FLOAT;
if (type == Double.class) return DOUBLE;
if (type == Void.class) return VOID;
return null;
}
@DontInline
private static RuntimeException wrapperTypeError(Class<?> type) {
for (Wrapper x : values())
if (x.wrapperType == type)
throw new InternalError(); // missing wrapper type
return newIllegalArgumentException("not wrapper: " + type);
}
/** Return the wrapper that corresponds to the given bytecode
* signature character. Return {@code OBJECT} for the character 'L'.
* @throws IllegalArgumentException for any non-signature character or {@code '['}.
*/
public static Wrapper forBasicType(char type) {
Wrapper w = FROM_CHAR[hashChar(type)];
Wrapper w = FROM_CHAR[(type + (type >> 1)) & 0xf];
if (w != null && w.basicTypeChar == type) {
return w;
}
for (Wrapper x : values())
if (w.basicTypeChar == type)
throw basicTypeError(type);
}
@DontInline
private static RuntimeException basicTypeError(char type) {
for (Wrapper x : values()) {
if (x.basicTypeChar == type) {
throw new InternalError(); // redo hash function
throw newIllegalArgumentException("not basic type char: "+type);
}
}
return newIllegalArgumentException("not basic type char: " + type);
}
/** Return the wrapper for the given type, if it is
* a primitive type, else return {@code OBJECT}.
*/
public static Wrapper forBasicType(Class<?> type) {
if (type.isPrimitive())
return forPrimitiveType(type);
if (type == int.class) return INT;
if (type == long.class) return LONG;
if (type == boolean.class) return BOOLEAN;
if (type == void.class) return VOID;
if (type == byte.class) return BYTE;
if (type == char.class) return CHAR;
if (type == float.class) return FLOAT;
if (type == double.class) return DOUBLE;
if (type == short.class) return SHORT;
return OBJECT; // any reference, including wrappers or arrays
}
// Note on perfect hashes:
// for signature chars c, do (c + (c >> 1)) % 16
// for primitive type names n, do (n[0] + n[2]) % 16
// The type name hash works for both primitive and wrapper names.
// You can add "java/lang/Object" to the primitive names.
// But you add the wrapper name Object, use (n[2] + (3*n[1])) % 16.
private static final Wrapper[] FROM_PRIM = new Wrapper[16];
private static final Wrapper[] FROM_WRAP = new Wrapper[16];
// for signature chars c, do (c + (c >> 1)) & 0xf
private static final Wrapper[] FROM_CHAR = new Wrapper[16];
private static int hashPrim(Class<?> x) {
String xn = x.getName();
if (xn.length() < 3) return 0;
return (xn.charAt(0) + xn.charAt(2)) % 16;
}
private static int hashWrap(Class<?> x) {
String xn = x.getName();
final int offset = 10; assert(offset == "java.lang.".length());
if (xn.length() < offset+3) return 0;
return (3*xn.charAt(offset+1) + xn.charAt(offset+2)) % 16;
}
private static int hashChar(char x) {
return (x + (x >> 1)) % 16;
}
static {
for (Wrapper w : values()) {
int pi = hashPrim(w.primitiveType);
int wi = hashWrap(w.wrapperType);
int ci = hashChar(w.basicTypeChar);
assert(FROM_PRIM[pi] == null);
assert(FROM_WRAP[wi] == null);
assert(FROM_CHAR[ci] == null);
FROM_PRIM[pi] = w;
FROM_WRAP[wi] = w;
FROM_CHAR[ci] = w;
FROM_CHAR[(w.basicTypeChar + (w.basicTypeChar >> 1)) & 0xf] = w;
}
//assert(jdk.sun.invoke.util.WrapperTest.test(false));
}
/** What is the primitive type wrapped by this wrapper? */
@ -474,13 +457,6 @@ public enum Wrapper {
*/
public String primitiveSimpleName() { return primitiveSimpleName; }
// /** Wrap a value in the given type, which may be either a primitive or wrapper type.
// * Performs standard primitive conversions, including truncation and float conversions.
// */
// public static <T> T wrap(Object x, Class<T> type) {
// return Wrapper.valueOf(type).cast(x, type);
// }
/** Cast a wrapped value to the given type, which may be either a primitive or wrapper type.
* The given target type must be this wrapper's primitive or wrapper type.
* If this wrapper is OBJECT, the target type may also be an interface, perform no runtime check.

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.lang.invoke;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.concurrent.TimeUnit;
import sun.invoke.util.Wrapper;
/**
* Test sun.invoke.util.Wrapper accessors
*/
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(value = 3, jvmArgsAppend = "--add-exports=java.base/sun.invoke.util=ALL-UNNAMED")
public class Wrappers {
public static Class<?>[] PRIM_CLASSES = {
int.class,
float.class,
short.class,
double.class,
void.class,
boolean.class,
byte.class,
char.class,
long.class };
public static Class<?>[] WRAP_CLASSES = {
Integer.class,
Float.class,
Short.class,
Double.class,
Void.class,
Boolean.class,
Byte.class,
Character.class,
Long.class,
Object.class };
public static char[] BASIC_TYPES = {
'I',
'J',
'S',
'B',
'C',
'F',
'D',
'Z',
'V',
'L' };
public static char[] PRIM_TYPES = {
'I',
'J',
'S',
'B',
'C',
'F',
'D',
'Z',
'V' };
@Benchmark
public void forPrimitive(Blackhole bh) throws Throwable {
for (Class<?> c : PRIM_CLASSES) {
bh.consume(Wrapper.forPrimitiveType(c));
}
}
@Benchmark
public void forWrapper(Blackhole bh) throws Throwable {
for (Class<?> c : WRAP_CLASSES) {
bh.consume(Wrapper.forWrapperType(c));
}
}
@Benchmark
public void forBasicType(Blackhole bh) throws Throwable {
for (char c : BASIC_TYPES) {
bh.consume(Wrapper.forBasicType(c));
}
}
@Benchmark
public void forPrimitiveType(Blackhole bh) throws Throwable {
for (char c : PRIM_TYPES) {
bh.consume(Wrapper.forPrimitiveType(c));
}
}
}