mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8284880: Re-examine sun.invoke.util.Wrapper hash tables
Reviewed-by: erikj, mchung
This commit is contained in:
parent
e307bc8694
commit
5df8bd6b4e
@ -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, \
|
||||
))
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
122
test/micro/org/openjdk/bench/java/lang/invoke/Wrappers.java
Normal file
122
test/micro/org/openjdk/bench/java/lang/invoke/Wrappers.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user