8279930: Synthetic cast causes generation of store barriers when using heap segments

Reviewed-by: psandoz
This commit is contained in:
Maurizio Cimadamore 2022-01-14 11:15:16 +00:00
parent 45f20633f6
commit c6b027559c
3 changed files with 292 additions and 29 deletions
src/jdk.incubator.foreign/share/classes/jdk
incubator/foreign
internal/foreign
test/micro/org/openjdk/bench/jdk/incubator/foreign

@ -266,7 +266,7 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory
* <p>
* The returned spliterator splits this segment according to the specified element layout; that is,
* if the supplied layout has size N, then calling {@link Spliterator#trySplit()} will result in a spliterator serving
* approximately {@code S/N/2} elements (depending on whether N is even or not), where {@code S} is the size of
* approximately {@code S/N} elements (depending on whether N is even or not), where {@code S} is the size of
* this segment. As such, splitting is possible as long as {@code S/N >= 2}. The spliterator returns segments that
* are associated with the same scope as this segment.
* <p>

@ -36,13 +36,17 @@ import java.nio.ByteBuffer;
import java.util.Objects;
/**
* Implementation for heap memory segments. An heap memory segment is composed by an offset and
* Implementation for heap memory segments. A heap memory segment is composed by an offset and
* a base object (typically an array). To enhance performances, the access to the base object needs to feature
* sharp type information, as well as sharp null-check information. For this reason, many concrete subclasses
* of {@link HeapMemorySegmentImpl} are defined (e.g. {@link OfFloat}, so that each subclass can override the
* {@link HeapMemorySegmentImpl#base()} method so that it returns an array of the correct (sharp) type.
* {@link HeapMemorySegmentImpl#base()} method so that it returns an array of the correct (sharp) type. Note that
* the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses
* accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as
* using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations,
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, int, ResourceScopeImpl)}.
*/
public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl {
public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
@ -53,17 +57,17 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
private static final long MAX_ALIGN_8 = 8;
final long offset;
final H base;
final Object base;
@ForceInline
HeapMemorySegmentImpl(long offset, H base, long length, int mask) {
HeapMemorySegmentImpl(long offset, Object base, long length, int mask) {
super(length, mask, ResourceScopeImpl.GLOBAL);
this.offset = offset;
this.base = base;
}
@Override
abstract H base();
abstract Object base();
@Override
long min() {
@ -71,7 +75,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
@Override
abstract HeapMemorySegmentImpl<H> dup(long offset, long size, int mask, ResourceScopeImpl scope);
abstract HeapMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope);
@Override
ByteBuffer makeByteBuffer() {
@ -84,9 +88,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
// factories
public static class OfByte extends HeapMemorySegmentImpl<byte[]> {
public static class OfByte extends HeapMemorySegmentImpl {
OfByte(long offset, byte[] base, long length, int mask) {
OfByte(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -97,7 +101,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
byte[] base() {
return Objects.requireNonNull(base);
return (byte[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(byte[] arr) {
@ -112,9 +116,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfChar extends HeapMemorySegmentImpl<char[]> {
public static class OfChar extends HeapMemorySegmentImpl {
OfChar(long offset, char[] base, long length, int mask) {
OfChar(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -125,7 +129,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
char[] base() {
return Objects.requireNonNull(base);
return (char[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(char[] arr) {
@ -140,9 +144,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfShort extends HeapMemorySegmentImpl<short[]> {
public static class OfShort extends HeapMemorySegmentImpl {
OfShort(long offset, short[] base, long length, int mask) {
OfShort(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -153,7 +157,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
short[] base() {
return Objects.requireNonNull(base);
return (short[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(short[] arr) {
@ -168,9 +172,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfInt extends HeapMemorySegmentImpl<int[]> {
public static class OfInt extends HeapMemorySegmentImpl {
OfInt(long offset, int[] base, long length, int mask) {
OfInt(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -181,7 +185,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
int[] base() {
return Objects.requireNonNull(base);
return (int[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(int[] arr) {
@ -196,9 +200,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfLong extends HeapMemorySegmentImpl<long[]> {
public static class OfLong extends HeapMemorySegmentImpl {
OfLong(long offset, long[] base, long length, int mask) {
OfLong(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -209,7 +213,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
long[] base() {
return Objects.requireNonNull(base);
return (long[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(long[] arr) {
@ -224,9 +228,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfFloat extends HeapMemorySegmentImpl<float[]> {
public static class OfFloat extends HeapMemorySegmentImpl {
OfFloat(long offset, float[] base, long length, int mask) {
OfFloat(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -237,7 +241,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
float[] base() {
return Objects.requireNonNull(base);
return (float[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(float[] arr) {
@ -252,9 +256,9 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
}
}
public static class OfDouble extends HeapMemorySegmentImpl<double[]> {
public static class OfDouble extends HeapMemorySegmentImpl {
OfDouble(long offset, double[] base, long length, int mask) {
OfDouble(long offset, Object base, long length, int mask) {
super(offset, base, length, mask);
}
@ -265,7 +269,7 @@ public abstract class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl
@Override
double[] base() {
return Objects.requireNonNull(base);
return (double[])Objects.requireNonNull(base);
}
public static MemorySegment fromArray(double[] arr) {

@ -0,0 +1,259 @@
/*
* 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.jdk.incubator.foreign;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope;
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.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" })
public class LoopOverSlice {
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
MemorySegment nativeSegment, heapSegment;
IntBuffer nativeBuffer, heapBuffer;
ResourceScope scope;
@Setup
public void setup() {
scope = ResourceScope.newConfinedScope();
nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, scope);
heapSegment = MemorySegment.ofArray(new int[ELEM_SIZE]);
nativeBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
heapBuffer = IntBuffer.wrap(new int[ELEM_SIZE]);
}
@TearDown
public void tearDown() {
scope.close();
}
@Benchmark
public void native_segment_slice_loop() {
new NativeSegmentWrapper(nativeSegment).forEach(NativeSegmentWrapper.Element::get);
}
@Benchmark
public void native_buffer_slice_loop() {
new NativeBufferWrapper(nativeBuffer).forEach(NativeBufferWrapper.Element::get);
}
@Benchmark
public void heap_segment_slice_loop() {
new HeapSegmentWrapper(heapSegment).forEach(HeapSegmentWrapper.Element::get);
}
@Benchmark
public void heap_buffer_slice_loop() {
new HeapBufferWrapper(heapBuffer).forEach(HeapBufferWrapper.Element::get);
}
class HeapSegmentWrapper implements Iterable<HeapSegmentWrapper.Element> {
final MemorySegment segment;
public HeapSegmentWrapper(MemorySegment segment) {
this.segment = segment;
}
@Override
public Iterator<Element> iterator() {
return new Iterator<Element>() {
MemorySegment current = segment;
@Override
public boolean hasNext() {
return current.byteSize() > 4;
}
@Override
public Element next() {
Element element = new Element(current);
current = current.asSlice(4);
return element;
}
};
}
static class Element {
final MemorySegment segment;
public Element(MemorySegment segment) {
this.segment = segment;
}
int get() {
return segment.getAtIndex(JAVA_INT, 0);
}
}
}
class NativeSegmentWrapper implements Iterable<NativeSegmentWrapper.Element> {
final MemorySegment segment;
public NativeSegmentWrapper(MemorySegment segment) {
this.segment = segment;
}
@Override
public Iterator<Element> iterator() {
return new Iterator<Element>() {
MemorySegment current = segment;
@Override
public boolean hasNext() {
return current.byteSize() > 4;
}
@Override
public Element next() {
Element element = new Element(current);
current = current.asSlice(4);
return element;
}
};
}
static class Element {
final MemorySegment segment;
public Element(MemorySegment segment) {
this.segment = segment;
}
int get() {
return segment.getAtIndex(JAVA_INT, 0);
}
}
}
class NativeBufferWrapper implements Iterable<NativeBufferWrapper.Element> {
final IntBuffer buffer;
public NativeBufferWrapper(IntBuffer buffer) {
this.buffer = buffer;
}
@Override
public Iterator<Element> iterator() {
return new Iterator<Element>() {
IntBuffer current = buffer;
@Override
public boolean hasNext() {
return current.position() < current.limit();
}
@Override
public Element next() {
Element element = new Element(current);
int lim = current.limit();
current = current.slice(1, lim - 1);
return element;
}
};
}
static class Element {
final IntBuffer buffer;
public Element(IntBuffer segment) {
this.buffer = segment;
}
int get() {
return buffer.get( 0);
}
}
}
class HeapBufferWrapper implements Iterable<HeapBufferWrapper.Element> {
final IntBuffer buffer;
public HeapBufferWrapper(IntBuffer buffer) {
this.buffer = buffer;
}
@Override
public Iterator<Element> iterator() {
return new Iterator<Element>() {
IntBuffer current = buffer;
@Override
public boolean hasNext() {
return current.position() < current.limit();
}
@Override
public Element next() {
Element element = new Element(current);
int lim = current.limit();
current = current.slice(1, lim - 1);
return element;
}
};
}
static class Element {
final IntBuffer buffer;
public Element(IntBuffer segment) {
this.buffer = segment;
}
int get() {
return buffer.get( 0);
}
}
}
}