HttpObjectDecoder performance improvements
Motivation: The HttpObjectDecoder is on the hot code path for the http codec. There are a few hot methods which can be modified to improve performance. Modifications: - Modify AppendableCharSequence to provide unsafe methods which don't need to re-check bounds for every call. - Update HttpObjectDecoder methods to take advantage of new AppendableCharSequence methods. Result: Peformance boost for decoding http objects.
This commit is contained in:
parent
dbc078ccf4
commit
9d347ffb91
@ -666,9 +666,9 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
|
|||||||
cEnd = findEndOfString(sb);
|
cEnd = findEndOfString(sb);
|
||||||
|
|
||||||
return new String[] {
|
return new String[] {
|
||||||
sb.substring(aStart, aEnd),
|
sb.subStringUnsafe(aStart, aEnd),
|
||||||
sb.substring(bStart, bEnd),
|
sb.subStringUnsafe(bStart, bEnd),
|
||||||
cStart < cEnd? sb.substring(cStart, cEnd) : "" };
|
cStart < cEnd? sb.subStringUnsafe(cStart, cEnd) : "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
private void splitHeader(AppendableCharSequence sb) {
|
private void splitHeader(AppendableCharSequence sb) {
|
||||||
@ -694,44 +694,41 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name = sb.substring(nameStart, nameEnd);
|
name = sb.subStringUnsafe(nameStart, nameEnd);
|
||||||
valueStart = findNonWhitespace(sb, colonEnd);
|
valueStart = findNonWhitespace(sb, colonEnd);
|
||||||
if (valueStart == length) {
|
if (valueStart == length) {
|
||||||
value = EMPTY_VALUE;
|
value = EMPTY_VALUE;
|
||||||
} else {
|
} else {
|
||||||
valueEnd = findEndOfString(sb);
|
valueEnd = findEndOfString(sb);
|
||||||
value = sb.substring(valueStart, valueEnd);
|
value = sb.subStringUnsafe(valueStart, valueEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findNonWhitespace(CharSequence sb, int offset) {
|
private static int findNonWhitespace(AppendableCharSequence sb, int offset) {
|
||||||
int result;
|
for (int result = offset; result < sb.length(); ++result) {
|
||||||
for (result = offset; result < sb.length(); result ++) {
|
if (!Character.isWhitespace(sb.charAtUnsafe(result))) {
|
||||||
if (!Character.isWhitespace(sb.charAt(result))) {
|
return result;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return sb.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findWhitespace(CharSequence sb, int offset) {
|
private static int findWhitespace(AppendableCharSequence sb, int offset) {
|
||||||
int result;
|
for (int result = offset; result < sb.length(); ++result) {
|
||||||
for (result = offset; result < sb.length(); result ++) {
|
if (Character.isWhitespace(sb.charAtUnsafe(result))) {
|
||||||
if (Character.isWhitespace(sb.charAt(result))) {
|
return result;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return sb.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findEndOfString(CharSequence sb) {
|
private static int findEndOfString(AppendableCharSequence sb) {
|
||||||
int result;
|
for (int result = sb.length() - 1; result > 0; --result) {
|
||||||
for (result = sb.length(); result > 0; result --) {
|
if (!Character.isWhitespace(sb.charAtUnsafe(result))) {
|
||||||
if (!Character.isWhitespace(sb.charAt(result - 1))) {
|
return result + 1;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class HeaderParser implements ByteProcessor {
|
private static class HeaderParser implements ByteProcessor {
|
||||||
|
@ -30,6 +30,9 @@ public final class AppendableCharSequence implements CharSequence, Appendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AppendableCharSequence(char[] chars) {
|
private AppendableCharSequence(char[] chars) {
|
||||||
|
if (chars.length < 1) {
|
||||||
|
throw new IllegalArgumentException("length: " + chars.length + " (length: >= 1)");
|
||||||
|
}
|
||||||
this.chars = chars;
|
this.chars = chars;
|
||||||
pos = chars.length;
|
pos = chars.length;
|
||||||
}
|
}
|
||||||
@ -47,6 +50,17 @@ public final class AppendableCharSequence implements CharSequence, Appendable {
|
|||||||
return chars[index];
|
return chars[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access a value in this {@link CharSequence}.
|
||||||
|
* This method is considered unsafe as index values are assumed to be legitimate.
|
||||||
|
* Only underlying array bounds checking is done.
|
||||||
|
* @param index The index to access the underlying array at.
|
||||||
|
* @return The value at {@code index}.
|
||||||
|
*/
|
||||||
|
public char charAtUnsafe(int index) {
|
||||||
|
return chars[index];
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppendableCharSequence subSequence(int start, int end) {
|
public AppendableCharSequence subSequence(int start, int end) {
|
||||||
return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
|
return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
|
||||||
@ -54,17 +68,12 @@ public final class AppendableCharSequence implements CharSequence, Appendable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppendableCharSequence append(char c) {
|
public AppendableCharSequence append(char c) {
|
||||||
if (pos == chars.length) {
|
try {
|
||||||
char[] old = chars;
|
chars[pos++] = c;
|
||||||
// double it
|
} catch (IndexOutOfBoundsException e) {
|
||||||
int len = old.length << 1;
|
expand();
|
||||||
if (len < 0) {
|
chars[pos - 1] = c;
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
chars = new char[len];
|
|
||||||
System.arraycopy(old, 0, chars, 0, old.length);
|
|
||||||
}
|
}
|
||||||
chars[pos++] = c;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +130,26 @@ public final class AppendableCharSequence implements CharSequence, Appendable {
|
|||||||
return new String(chars, start, length);
|
return new String(chars, start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link String} from the given start to end.
|
||||||
|
* This method is considered unsafe as index values are assumed to be legitimate.
|
||||||
|
* Only underlying array bounds checking is done.
|
||||||
|
*/
|
||||||
|
public String subStringUnsafe(int start, int end) {
|
||||||
|
return new String(chars, start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand() {
|
||||||
|
char[] old = chars;
|
||||||
|
// double it
|
||||||
|
int len = old.length << 1;
|
||||||
|
if (len < 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
chars = new char[len];
|
||||||
|
System.arraycopy(old, 0, chars, 0, old.length);
|
||||||
|
}
|
||||||
|
|
||||||
private static char[] expand(char[] array, int neededSpace, int size) {
|
private static char[] expand(char[] array, int neededSpace, int size) {
|
||||||
int newCapacity = array.length;
|
int newCapacity = array.length;
|
||||||
do {
|
do {
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||||
|
* copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package io.netty.microbenchmark.common;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.Level;
|
||||||
|
import org.openjdk.jmh.annotations.Measurement;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
|
import org.openjdk.jmh.annotations.Threads;
|
||||||
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
|
||||||
|
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||||
|
|
||||||
|
@Threads(1)
|
||||||
|
@Warmup(iterations = 5)
|
||||||
|
@Measurement(iterations = 5)
|
||||||
|
public class AppendableCharSequenceBenchmark extends AbstractMicrobenchmark {
|
||||||
|
@Param({ "32", "64", "128", "256" })
|
||||||
|
private int charsInitSize;
|
||||||
|
|
||||||
|
@Param({ "10", "100", "10000", "1000000" })
|
||||||
|
private int simulatedDataSize;
|
||||||
|
|
||||||
|
private static final Random rand = new Random();
|
||||||
|
private char[] chars;
|
||||||
|
private char simulatedData;
|
||||||
|
private int pos;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setup() {
|
||||||
|
chars = new char[charsInitSize];
|
||||||
|
simulatedData = (char) rand.nextInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void appendCheckBeforeCopy() {
|
||||||
|
checkReset();
|
||||||
|
if (pos == chars.length) {
|
||||||
|
expand();
|
||||||
|
}
|
||||||
|
chars[pos++] = simulatedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void appendCatchExceptionAfter() {
|
||||||
|
checkReset();
|
||||||
|
try {
|
||||||
|
chars[pos++] = simulatedData;
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
expand();
|
||||||
|
chars[pos - 1] = simulatedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkReset() {
|
||||||
|
if (pos == simulatedDataSize) {
|
||||||
|
pos = 0;
|
||||||
|
chars = new char[charsInitSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand() {
|
||||||
|
char[] old = chars;
|
||||||
|
// double it
|
||||||
|
int len = old.length << 1;
|
||||||
|
if (len < 0) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
chars = new char[len];
|
||||||
|
System.arraycopy(old, 0, chars, 0, old.length);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user