8345465: Fix performance regression on x64 after JDK-8345120

Reviewed-by: mcimadamore
This commit is contained in:
Per Minborg 2024-12-10 10:01:27 +00:00
parent 2979806c72
commit 06c44dd568
2 changed files with 29 additions and 24 deletions

View file

@ -130,8 +130,8 @@ public final class StringSupport {
final long toOffset) { final long toOffset) {
final long length = toOffset - fromOffset; final long length = toOffset - fromOffset;
segment.checkBounds(fromOffset, length); segment.checkBounds(fromOffset, length);
if (length == 0) { if (length < Byte.BYTES) {
// The state has to be checked explicitly for zero-length segments // There can be no null terminator present
segment.scope.checkValidState(); segment.scope.checkValidState();
throw nullNotFound(segment, fromOffset, toOffset); throw nullNotFound(segment, fromOffset, toOffset);
} }
@ -164,7 +164,8 @@ public final class StringSupport {
final long toOffset) { final long toOffset) {
final long length = toOffset - fromOffset; final long length = toOffset - fromOffset;
segment.checkBounds(fromOffset, length); segment.checkBounds(fromOffset, length);
if (length == 0) { if (length < Short.BYTES) {
// There can be no null terminator present
segment.scope.checkValidState(); segment.scope.checkValidState();
throw nullNotFound(segment, fromOffset, toOffset); throw nullNotFound(segment, fromOffset, toOffset);
} }
@ -199,13 +200,16 @@ public final class StringSupport {
final long toOffset) { final long toOffset) {
final long length = toOffset - fromOffset; final long length = toOffset - fromOffset;
segment.checkBounds(fromOffset, length); segment.checkBounds(fromOffset, length);
if (length == 0) { if (length < Integer.BYTES) {
// There can be no null terminator present
segment.scope.checkValidState(); segment.scope.checkValidState();
throw nullNotFound(segment, fromOffset, toOffset); throw nullNotFound(segment, fromOffset, toOffset);
} }
long offset = fromOffset;
// For quad byte strings, it does not pay off to use long scanning on x64
if (!Architecture.isX64()) {
final long longBytes = length & LONG_MASK; final long longBytes = length & LONG_MASK;
final long longLimit = fromOffset + longBytes; final long longLimit = fromOffset + longBytes;
long offset = fromOffset;
for (; offset < longLimit; offset += Long.BYTES) { for (; offset < longLimit; offset += Long.BYTES) {
long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian());
if (mightContainZeroInt(val)) { if (mightContainZeroInt(val)) {
@ -216,6 +220,7 @@ public final class StringSupport {
} }
} }
} }
}
// Handle the tail // Handle the tail
// Prevent over scanning as we step by 4 // Prevent over scanning as we step by 4
final long endScan = toOffset & ~3; // The last two bit are zero final long endScan = toOffset & ~3; // The last two bit are zero

View file

@ -55,10 +55,10 @@ import static java.lang.foreign.ValueLayout.*;
"--enable-native-access=ALL-UNNAMED"}) "--enable-native-access=ALL-UNNAMED"})
public class InternalStrLen { public class InternalStrLen {
private AbstractMemorySegmentImpl singleByteSegment; private MemorySegment singleByteSegment;
private AbstractMemorySegmentImpl singleByteSegmentMisaligned; private MemorySegment singleByteSegmentMisaligned;
private AbstractMemorySegmentImpl doubleByteSegment; private MemorySegment doubleByteSegment;
private AbstractMemorySegmentImpl quadByteSegment; private MemorySegment quadByteSegment;
@Param({"1", "4", "16", "251", "1024"}) @Param({"1", "4", "16", "251", "1024"})
int size; int size;
@ -66,9 +66,9 @@ public class InternalStrLen {
@Setup @Setup
public void setup() { public void setup() {
var arena = Arena.ofAuto(); var arena = Arena.ofAuto();
singleByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Byte.BYTES); singleByteSegment = arena.allocate((size + 1L) * Byte.BYTES);
doubleByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Short.BYTES); doubleByteSegment = arena.allocate((size + 1L) * Short.BYTES);
quadByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Integer.BYTES); quadByteSegment = arena.allocate((size + 1L) * Integer.BYTES);
Stream.of(singleByteSegment, doubleByteSegment, quadByteSegment) Stream.of(singleByteSegment, doubleByteSegment, quadByteSegment)
.forEach(s -> IntStream.range(0, (int) s.byteSize() - 1) .forEach(s -> IntStream.range(0, (int) s.byteSize() - 1)
.forEach(i -> s.set( .forEach(i -> s.set(
@ -79,7 +79,7 @@ public class InternalStrLen {
singleByteSegment.set(ValueLayout.JAVA_BYTE, singleByteSegment.byteSize() - Byte.BYTES, (byte) 0); singleByteSegment.set(ValueLayout.JAVA_BYTE, singleByteSegment.byteSize() - Byte.BYTES, (byte) 0);
doubleByteSegment.set(ValueLayout.JAVA_SHORT, doubleByteSegment.byteSize() - Short.BYTES, (short) 0); doubleByteSegment.set(ValueLayout.JAVA_SHORT, doubleByteSegment.byteSize() - Short.BYTES, (short) 0);
quadByteSegment.set(ValueLayout.JAVA_INT, quadByteSegment.byteSize() - Integer.BYTES, 0); quadByteSegment.set(ValueLayout.JAVA_INT, quadByteSegment.byteSize() - Integer.BYTES, 0);
singleByteSegmentMisaligned = (AbstractMemorySegmentImpl) arena.allocate(singleByteSegment.byteSize() + 1). singleByteSegmentMisaligned = arena.allocate(singleByteSegment.byteSize() + 1).
asSlice(1); asSlice(1);
MemorySegment.copy(singleByteSegment, 0, singleByteSegmentMisaligned, 0, singleByteSegment.byteSize()); MemorySegment.copy(singleByteSegment, 0, singleByteSegmentMisaligned, 0, singleByteSegment.byteSize());
} }
@ -106,22 +106,22 @@ public class InternalStrLen {
@Benchmark @Benchmark
public int chunkedSingle() { public int chunkedSingle() {
return StringSupport.strlenByte(singleByteSegment, 0, singleByteSegment.byteSize()); return StringSupport.strlenByte((AbstractMemorySegmentImpl) singleByteSegment, 0, singleByteSegment.byteSize());
} }
@Benchmark @Benchmark
public int chunkedSingleMisaligned() { public int chunkedSingleMisaligned() {
return StringSupport.strlenByte(singleByteSegmentMisaligned, 0, singleByteSegment.byteSize()); return StringSupport.strlenByte((AbstractMemorySegmentImpl) singleByteSegmentMisaligned, 0, singleByteSegment.byteSize());
} }
@Benchmark @Benchmark
public int chunkedDouble() { public int chunkedDouble() {
return StringSupport.strlenShort(doubleByteSegment, 0, doubleByteSegment.byteSize()); return StringSupport.strlenShort((AbstractMemorySegmentImpl) doubleByteSegment, 0, doubleByteSegment.byteSize());
} }
@Benchmark @Benchmark
public int changedElementQuad() { public int changedElementQuad() {
return StringSupport.strlenInt(quadByteSegment, 0, quadByteSegment.byteSize()); return StringSupport.strlenInt((AbstractMemorySegmentImpl) quadByteSegment, 0, quadByteSegment.byteSize());
} }
// These are the legacy methods // These are the legacy methods