Backport InternalThreadLocalMap reusable ArrayList

Motivation:

See https://github.com/netty/netty/issues/3411.
Backport perf improvements on 4.0 and make AsyncHttpClient DNS modules
backports easier to maintain.

Modifications:

Cherry-picked b7415a3307

Result:

Reuse a thread local ArrayList to avoid allocations.
This commit is contained in:
Xiaoyan Lin 2016-01-30 20:14:47 -08:00 committed by Norman Maurer
parent fdefbba9a0
commit 4608ecf374
9 changed files with 43 additions and 16 deletions

View File

@ -22,8 +22,8 @@ import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparat
import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparatorOrNull;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.internal.InternalThreadLocalMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
@ -197,7 +197,7 @@ public final class ClientCookieEncoder extends CookieEncoder {
if (!cookiesIt.hasNext()) {
encode(buf, firstCookie);
} else {
List<Cookie> cookiesList = new ArrayList<Cookie>();
List<Cookie> cookiesList = InternalThreadLocalMap.get().arrayList();
cookiesList.add(firstCookie);
while (cookiesIt.hasNext()) {
cookiesList.add(cookiesIt.next());

View File

@ -28,6 +28,7 @@ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDec
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.StringUtil;
import java.io.IOException;
@ -1812,7 +1813,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* @return an array of String where values that were separated by ';' or ','
*/
private static String[] splitMultipartHeaderValues(String svalue) {
List<String> values = new ArrayList<String>(1);
List<String> values = InternalThreadLocalMap.get().arrayList(1);
boolean inQuote = false;
boolean escapeNext = false;
int start = 0;

View File

@ -22,6 +22,7 @@ import io.netty.util.concurrent.FastThreadLocalThread;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;
@ -34,6 +35,8 @@ import java.util.WeakHashMap;
*/
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
public static final Object UNSET = new Object();
public static InternalThreadLocalMap getIfSet() {
@ -144,6 +147,9 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap
if (charsetDecoderCache != null) {
count ++;
}
if (arrayList != null) {
count ++;
}
for (Object o: indexedVariables) {
if (o != UNSET) {
@ -182,6 +188,21 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap
return cache;
}
public <E> ArrayList<E> arrayList() {
return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY);
}
public <E> ArrayList<E> arrayList(int minCapacity) {
ArrayList<E> list = (ArrayList<E>) arrayList;
if (list == null) {
list = (ArrayList<E>) new ArrayList<Object>(minCapacity);
} else {
list.clear();
list.ensureCapacity(minCapacity);
}
return list;
}
public int futureListenerStackDepth() {
return futureListenerStackDepth;
}

View File

@ -16,7 +16,6 @@
package io.netty.util.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
@ -91,7 +90,7 @@ public final class StringUtil {
*/
public static String[] split(String value, char delim) {
final int end = value.length();
final List<String> res = new ArrayList<String>();
final List<String> res = InternalThreadLocalMap.get().arrayList();
int start = 0;
for (int i = 0; i < end; i ++) {
@ -133,7 +132,7 @@ public final class StringUtil {
*/
public static String[] split(String value, char delim, int maxParts) {
final int end = value.length();
final List<String> res = new ArrayList<String>();
final List<String> res = InternalThreadLocalMap.get().arrayList();
int start = 0;
int cpt = 1;

View File

@ -21,6 +21,7 @@ import io.netty.util.concurrent.FastThreadLocal;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@ -51,6 +52,9 @@ class UnpaddedInternalThreadLocalMap {
Map<Charset, CharsetEncoder> charsetEncoderCache;
Map<Charset, CharsetDecoder> charsetDecoderCache;
// ArrayList-related thread-locals
ArrayList<Object> arrayList;
UnpaddedInternalThreadLocalMap(Object[] indexedVariables) {
this.indexedVariables = indexedVariables;
}

View File

@ -15,7 +15,8 @@
*/
package io.netty.handler.ssl;
import java.util.ArrayList;
import io.netty.util.internal.InternalThreadLocalMap;
import java.util.List;
import java.util.Set;
@ -33,7 +34,7 @@ public final class IdentityCipherSuiteFilter implements CipherSuiteFilter {
if (ciphers == null) {
return defaultCiphers.toArray(new String[defaultCiphers.size()]);
} else {
List<String> newCiphers = new ArrayList<String>(supportedCiphers.size());
List<String> newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size());
for (String c : ciphers) {
if (c == null) {
break;

View File

@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
@ -42,7 +43,6 @@ import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@ -1025,7 +1025,7 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public String[] getEnabledProtocols() {
List<String> enabled = new ArrayList<String>();
List<String> enabled = InternalThreadLocalMap.get().arrayList();
// Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled
enabled.add(PROTOCOL_SSL_V2_HELLO);

View File

@ -15,8 +15,9 @@
*/
package io.netty.handler.ssl;
import io.netty.util.internal.InternalThreadLocalMap;
import javax.net.ssl.SSLEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -40,10 +41,10 @@ public final class SupportedCipherSuiteFilter implements CipherSuiteFilter {
final List<String> newCiphers;
if (ciphers == null) {
newCiphers = new ArrayList<String>(defaultCiphers.size());
newCiphers = InternalThreadLocalMap.get().arrayList(defaultCiphers.size());
ciphers = defaultCiphers;
} else {
newCiphers = new ArrayList<String>(supportedCiphers.size());
newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size());
}
for (String c : ciphers) {
if (c == null) {

View File

@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.InternalThreadLocalMap;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
@ -31,7 +32,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
@ -151,7 +151,7 @@ public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFact
throw new NullPointerException("fingerprints");
}
List<byte[]> list = new ArrayList<byte[]>();
List<byte[]> list = InternalThreadLocalMap.get().arrayList();
for (byte[] f: fingerprints) {
if (f == null) {
break;
@ -171,7 +171,7 @@ public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFact
throw new NullPointerException("fingerprints");
}
List<byte[]> list = new ArrayList<byte[]>();
List<byte[]> list = InternalThreadLocalMap.get().arrayList();
for (String f: fingerprints) {
if (f == null) {
break;