mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8333893: Optimization for StringBuilder append boolean & null
Reviewed-by: liach
This commit is contained in:
parent
c33a8f52b6
commit
5890d9438b
5 changed files with 113 additions and 52 deletions
|
@ -640,14 +640,11 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
|
||||||
int count = this.count;
|
int count = this.count;
|
||||||
byte[] val = this.value;
|
byte[] val = this.value;
|
||||||
if (isLatin1()) {
|
if (isLatin1()) {
|
||||||
val[count++] = 'n';
|
StringLatin1.putCharsAt(val, count, 'n', 'u', 'l', 'l');
|
||||||
val[count++] = 'u';
|
|
||||||
val[count++] = 'l';
|
|
||||||
val[count++] = 'l';
|
|
||||||
} else {
|
} else {
|
||||||
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
|
StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
|
||||||
}
|
}
|
||||||
this.count = count;
|
this.count = count + 4;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,25 +769,18 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
|
||||||
byte[] val = this.value;
|
byte[] val = this.value;
|
||||||
if (isLatin1()) {
|
if (isLatin1()) {
|
||||||
if (b) {
|
if (b) {
|
||||||
val[count++] = 't';
|
StringLatin1.putCharsAt(val, count, 't', 'r', 'u', 'e');
|
||||||
val[count++] = 'r';
|
|
||||||
val[count++] = 'u';
|
|
||||||
val[count++] = 'e';
|
|
||||||
} else {
|
} else {
|
||||||
val[count++] = 'f';
|
StringLatin1.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e');
|
||||||
val[count++] = 'a';
|
|
||||||
val[count++] = 'l';
|
|
||||||
val[count++] = 's';
|
|
||||||
val[count++] = 'e';
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (b) {
|
if (b) {
|
||||||
count = StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e');
|
StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e');
|
||||||
} else {
|
} else {
|
||||||
count = StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e');
|
StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.count = count;
|
this.count = count + (b ? 4 : 5);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
import jdk.internal.misc.Unsafe;
|
||||||
import jdk.internal.util.ArraysSupport;
|
import jdk.internal.util.ArraysSupport;
|
||||||
import jdk.internal.util.DecimalDigits;
|
import jdk.internal.util.DecimalDigits;
|
||||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||||
|
@ -42,6 +43,8 @@ import static java.lang.String.checkIndex;
|
||||||
import static java.lang.String.checkOffset;
|
import static java.lang.String.checkOffset;
|
||||||
|
|
||||||
final class StringLatin1 {
|
final class StringLatin1 {
|
||||||
|
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
|
|
||||||
public static char charAt(byte[] value, int index) {
|
public static char charAt(byte[] value, int index) {
|
||||||
checkIndex(index, value.length);
|
checkIndex(index, value.length);
|
||||||
return (char)(value[index] & 0xff);
|
return (char)(value[index] & 0xff);
|
||||||
|
@ -824,6 +827,27 @@ final class StringLatin1 {
|
||||||
return StreamSupport.stream(LinesSpliterator.spliterator(value), false);
|
return StreamSupport.stream(LinesSpliterator.spliterator(value), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4) {
|
||||||
|
assert index >= 0 && index + 3 < length(val) : "Trusted caller missed bounds check";
|
||||||
|
// Don't use the putChar method, Its instrinsic will cause C2 unable to combining values into larger stores.
|
||||||
|
long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + index;
|
||||||
|
UNSAFE.putByte(val, address , (byte)(c1));
|
||||||
|
UNSAFE.putByte(val, address + 1, (byte)(c2));
|
||||||
|
UNSAFE.putByte(val, address + 2, (byte)(c3));
|
||||||
|
UNSAFE.putByte(val, address + 3, (byte)(c4));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4, int c5) {
|
||||||
|
assert index >= 0 && index + 4 < length(val) : "Trusted caller missed bounds check";
|
||||||
|
// Don't use the putChar method, Its instrinsic will cause C2 unable to combining values into larger stores.
|
||||||
|
long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + index;
|
||||||
|
UNSAFE.putByte(val, address , (byte)(c1));
|
||||||
|
UNSAFE.putByte(val, address + 1, (byte)(c2));
|
||||||
|
UNSAFE.putByte(val, address + 2, (byte)(c3));
|
||||||
|
UNSAFE.putByte(val, address + 3, (byte)(c4));
|
||||||
|
UNSAFE.putByte(val, address + 4, (byte)(c5));
|
||||||
|
}
|
||||||
|
|
||||||
public static void putChar(byte[] val, int index, int c) {
|
public static void putChar(byte[] val, int index, int c) {
|
||||||
//assert (canEncode(c));
|
//assert (canEncode(c));
|
||||||
val[index] = (byte)(c);
|
val[index] = (byte)(c);
|
||||||
|
|
|
@ -43,7 +43,6 @@ import static java.lang.String.UTF16;
|
||||||
import static java.lang.String.LATIN1;
|
import static java.lang.String.LATIN1;
|
||||||
|
|
||||||
final class StringUTF16 {
|
final class StringUTF16 {
|
||||||
|
|
||||||
// Return a new byte array for a UTF16-coded string for len chars
|
// Return a new byte array for a UTF16-coded string for len chars
|
||||||
// Throw an exception if out of range
|
// Throw an exception if out of range
|
||||||
public static byte[] newBytesFor(int len) {
|
public static byte[] newBytesFor(int len) {
|
||||||
|
@ -1548,27 +1547,20 @@ final class StringUTF16 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
|
static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4) {
|
||||||
int end = i + 4;
|
assert index >= 0 && index + 3 < length(val) : "Trusted caller missed bounds check";
|
||||||
checkBoundsBeginEnd(i, end, value);
|
putChar(val, index , c1);
|
||||||
putChar(value, i++, c1);
|
putChar(val, index + 1, c2);
|
||||||
putChar(value, i++, c2);
|
putChar(val, index + 2, c3);
|
||||||
putChar(value, i++, c3);
|
putChar(val, index + 3, c4);
|
||||||
putChar(value, i++, c4);
|
|
||||||
assert(i == end);
|
|
||||||
return end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
|
static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4, int c5) {
|
||||||
int end = i + 5;
|
putChar(val, index , c1);
|
||||||
checkBoundsBeginEnd(i, end, value);
|
putChar(val, index + 1, c2);
|
||||||
putChar(value, i++, c1);
|
putChar(val, index + 2, c3);
|
||||||
putChar(value, i++, c2);
|
putChar(val, index + 3, c4);
|
||||||
putChar(value, i++, c3);
|
putChar(val, index + 4, c5);
|
||||||
putChar(value, i++, c4);
|
|
||||||
putChar(value, i++, c5);
|
|
||||||
assert(i == end);
|
|
||||||
return end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char charAt(byte[] value, int index) {
|
public static char charAt(byte[] value, int index) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -133,11 +133,17 @@ public class Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
|
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
|
||||||
return StringUTF16.putCharsAt(value, i, c1, c2, c3, c4);
|
int end = i + 4;
|
||||||
|
StringUTF16.checkBoundsBeginEnd(i, end, value);
|
||||||
|
StringUTF16.putCharsAt(value, i, c1, c2, c3, c4);
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
|
public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
|
||||||
return StringUTF16.putCharsAt(value, i, c1, c2, c3, c4, c5);
|
int end = i + 5;
|
||||||
|
StringUTF16.checkBoundsBeginEnd(i, end, value);
|
||||||
|
StringUTF16.putCharsAt(value, i, c1, c2, c3, c4, c5);
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char charAt(byte[] value, int index) {
|
public static char charAt(byte[] value, int index) {
|
||||||
|
|
|
@ -226,17 +226,66 @@ public class StringBuilders {
|
||||||
|
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public String toStringCharWithBool8() {
|
public int appendWithBool8Latin1() {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder buf = sbLatin1;
|
||||||
result.append(true);
|
buf.setLength(0);
|
||||||
result.append(false);
|
buf.append(true);
|
||||||
result.append(true);
|
buf.append(false);
|
||||||
result.append(true);
|
buf.append(true);
|
||||||
result.append(false);
|
buf.append(true);
|
||||||
result.append(true);
|
buf.append(false);
|
||||||
result.append(false);
|
buf.append(true);
|
||||||
result.append(false);
|
buf.append(false);
|
||||||
return result.toString();
|
buf.append(false);
|
||||||
|
return buf.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public int appendWithBool8Utf16() {
|
||||||
|
StringBuilder buf = sbUtf16;
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append(true);
|
||||||
|
buf.append(false);
|
||||||
|
buf.append(true);
|
||||||
|
buf.append(true);
|
||||||
|
buf.append(false);
|
||||||
|
buf.append(true);
|
||||||
|
buf.append(false);
|
||||||
|
buf.append(false);
|
||||||
|
return buf.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public int appendWithNull8Latin1() {
|
||||||
|
StringBuilder buf = sbLatin1;
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
return buf.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public int appendWithNull8Utf16() {
|
||||||
|
StringBuilder buf = sbUtf16;
|
||||||
|
buf.setLength(0);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
buf.append((String) null);
|
||||||
|
return buf.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue