Reintroduce string pooling in JsonReader.
This makes Hotspot slower. From my before/after measurements using ParseBenchmark, times in microseconds: TWEETS: 350 -> 370 (+6%) READER_SHORT: 77 -> 76 (-1%) READER_LONG: 870 -> 940 (+8%) But it makes Dalvik faster by a greater margin. These before/after measurements use times in milliseconds: TWEETS: 25 -> 20 (-20%) READER_SHORT: 5.6 -> 4.7 (-16%) READER_LONG: 52 -> 47 (-10%) It's a net win because we're saving a greater fraction of time, and because we're helping the platform that needs the most help. We're paying microseconds on Hotspot to gain milliseconds on Dalvik.
This commit is contained in:
parent
680bd75a85
commit
084047d80b
@ -213,6 +213,16 @@ public class JsonReader implements Closeable {
|
||||
private static final int PEEKED_NUMBER = 16;
|
||||
private static final int PEEKED_EOF = 17;
|
||||
|
||||
/* State machine when parsing numbers */
|
||||
private static final int NUMBER_CHAR_NONE = 0;
|
||||
private static final int NUMBER_CHAR_SIGN = 1;
|
||||
private static final int NUMBER_CHAR_DIGIT = 2;
|
||||
private static final int NUMBER_CHAR_DECIMAL = 3;
|
||||
private static final int NUMBER_CHAR_FRACTION_DIGIT = 4;
|
||||
private static final int NUMBER_CHAR_EXP_E = 5;
|
||||
private static final int NUMBER_CHAR_EXP_SIGN = 6;
|
||||
private static final int NUMBER_CHAR_EXP_DIGIT = 7;
|
||||
|
||||
/** The input JSON. */
|
||||
private final Reader in;
|
||||
|
||||
@ -253,6 +263,11 @@ public class JsonReader implements Closeable {
|
||||
*/
|
||||
private String peekedString;
|
||||
|
||||
/**
|
||||
* A pool of short strings intended to prevent object allocation.
|
||||
*/
|
||||
private static final StringPool stringPool = new StringPool();
|
||||
|
||||
/*
|
||||
* The nesting stack. Using a manual array rather than an ArrayList saves 20%.
|
||||
*/
|
||||
@ -624,15 +639,6 @@ public class JsonReader implements Closeable {
|
||||
return peeked = peeking;
|
||||
}
|
||||
|
||||
private static final int NUMBER_CHAR_NONE = 0;
|
||||
private static final int NUMBER_CHAR_SIGN = 1;
|
||||
private static final int NUMBER_CHAR_DIGIT = 2;
|
||||
private static final int NUMBER_CHAR_DECIMAL = 3;
|
||||
private static final int NUMBER_CHAR_FRACTION_DIGIT = 4;
|
||||
private static final int NUMBER_CHAR_EXP_E = 5;
|
||||
private static final int NUMBER_CHAR_EXP_SIGN = 6;
|
||||
private static final int NUMBER_CHAR_EXP_DIGIT = 7;
|
||||
|
||||
private int peekNumber() throws IOException {
|
||||
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
|
||||
char[] buffer = this.buffer;
|
||||
@ -974,6 +980,7 @@ public class JsonReader implements Closeable {
|
||||
// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
|
||||
char[] buffer = this.buffer;
|
||||
StringBuilder builder = null;
|
||||
int hashCode = 0;
|
||||
while (true) {
|
||||
int p = pos;
|
||||
int l = limit;
|
||||
@ -985,7 +992,7 @@ public class JsonReader implements Closeable {
|
||||
if (c == quote) {
|
||||
pos = p;
|
||||
if (builder == null) {
|
||||
return new String(buffer, start, p - start - 1);
|
||||
return stringPool.get(buffer, start, p - start - 1, hashCode);
|
||||
} else {
|
||||
builder.append(buffer, start, p - start - 1);
|
||||
return builder.toString();
|
||||
@ -1003,8 +1010,11 @@ public class JsonReader implements Closeable {
|
||||
start = p;
|
||||
|
||||
} else if (c == '\n') {
|
||||
hashCode = (hashCode * 31) + c;
|
||||
lineNumber++;
|
||||
lineStart = p;
|
||||
} else {
|
||||
hashCode = (hashCode * 31) + c;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,20 +19,25 @@ package com.google.gson.stream;
|
||||
/**
|
||||
* A pool of string instances. Unlike the {@link String#intern() VM's
|
||||
* interned strings}, this pool provides no guarantee of reference equality.
|
||||
* It is intended only to save allocations. This class is not thread safe.
|
||||
* It is intended only to save allocations.
|
||||
*
|
||||
* <p>This class is safe for concurrent use.
|
||||
*/
|
||||
final class StringPool {
|
||||
|
||||
private final String[] pool = new String[512];
|
||||
/**
|
||||
* The maximum length of strings to add to the pool. Strings longer than this
|
||||
* don't benefit from pooling because we spend more time on pooling than we
|
||||
* save on garbage collection.
|
||||
*/
|
||||
private static final int MAX_LENGTH = 20;
|
||||
private final String[] pool = new String[1024];
|
||||
|
||||
/**
|
||||
* Returns a string equal to {@code new String(array, start, length)}.
|
||||
*/
|
||||
public String get(char[] array, int start, int length) {
|
||||
// Compute an arbitrary hash of the content
|
||||
int hashCode = 0;
|
||||
for (int i = start; i < start + length; i++) {
|
||||
hashCode = (hashCode * 31) + array[i];
|
||||
public String get(char[] array, int start, int length, int hashCode) {
|
||||
if (length > StringPool.MAX_LENGTH) {
|
||||
return new String(array, start, length);
|
||||
}
|
||||
|
||||
// Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
|
||||
|
Loading…
Reference in New Issue
Block a user