Permalink
Browse files

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.
  • Loading branch information...
swankjesse committed Sep 10, 2012
1 parent 680bd75 commit 084047d80b582317f382536604373cafa14583a4
@@ -213,6 +213,16 @@
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 @@
*/
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 @@ private int peekKeyword() throws IOException {
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 @@ private String nextQuotedValue(char quote) throws IOException {
// 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 @@ private String nextQuotedValue(char quote) throws IOException {
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 @@ private String nextQuotedValue(char quote) throws IOException {
start = p;
} else if (c == '\n') {
hashCode = (hashCode * 31) + c;
lineNumber++;
lineStart = p;
} else {
hashCode = (hashCode * 31) + c;
}
}
@@ -19,20 +19,25 @@
/**
* 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)

0 comments on commit 084047d

Please sign in to comment.