From 4608ecf3749d523d8be6b45c39d1022c9a73de8d Mon Sep 17 00:00:00 2001 From: Xiaoyan Lin Date: Sat, 30 Jan 2016 20:14:47 -0800 Subject: [PATCH] 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 b7415a3307d9a36f2a67339c7596e0ee2b2a5644 Result: Reuse a thread local ArrayList to avoid allocations. --- .../http/cookie/ClientCookieEncoder.java | 4 ++-- .../HttpPostMultipartRequestDecoder.java | 3 ++- .../util/internal/InternalThreadLocalMap.java | 21 +++++++++++++++++++ .../io/netty/util/internal/StringUtil.java | 5 ++--- .../UnpaddedInternalThreadLocalMap.java | 4 ++++ .../ssl/IdentityCipherSuiteFilter.java | 5 +++-- .../io/netty/handler/ssl/OpenSslEngine.java | 4 ++-- .../ssl/SupportedCipherSuiteFilter.java | 7 ++++--- .../util/FingerprintTrustManagerFactory.java | 6 +++--- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java index fcb444d28c..61773355d5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java @@ -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 cookiesList = new ArrayList(); + List cookiesList = InternalThreadLocalMap.get().arrayList(); cookiesList.add(firstCookie); while (cookiesIt.hasNext()) { cookiesList.add(cookiesIt.next()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java index ececbc218f..f83780c518 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java @@ -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 values = new ArrayList(1); + List values = InternalThreadLocalMap.get().arrayList(1); boolean inQuote = false; boolean escapeNext = false; int start = 0; diff --git a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java index 7374138bdb..25bf537556 100644 --- a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java @@ -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 ArrayList arrayList() { + return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY); + } + + public ArrayList arrayList(int minCapacity) { + ArrayList list = (ArrayList) arrayList; + if (list == null) { + list = (ArrayList) new ArrayList(minCapacity); + } else { + list.clear(); + list.ensureCapacity(minCapacity); + } + return list; + } + public int futureListenerStackDepth() { return futureListenerStackDepth; } diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java index d554770826..5eee513b66 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/netty/util/internal/StringUtil.java @@ -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 res = new ArrayList(); + final List 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 res = new ArrayList(); + final List res = InternalThreadLocalMap.get().arrayList(); int start = 0; int cpt = 1; diff --git a/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java index e1849deb8d..2d0bb6cd15 100644 --- a/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java @@ -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 charsetEncoderCache; Map charsetDecoderCache; + // ArrayList-related thread-locals + ArrayList arrayList; + UnpaddedInternalThreadLocalMap(Object[] indexedVariables) { this.indexedVariables = indexedVariables; } diff --git a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java index c9ebfb0da2..fc6235c2aa 100644 --- a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java +++ b/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java @@ -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 newCiphers = new ArrayList(supportedCiphers.size()); + List newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size()); for (String c : ciphers) { if (c == null) { break; diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java index a55b9c8c1c..fa8efd0a16 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java @@ -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 enabled = new ArrayList(); + List 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); diff --git a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java index 132f1a0e5e..a90f3c8ffc 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java +++ b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java @@ -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 newCiphers; if (ciphers == null) { - newCiphers = new ArrayList(defaultCiphers.size()); + newCiphers = InternalThreadLocalMap.get().arrayList(defaultCiphers.size()); ciphers = defaultCiphers; } else { - newCiphers = new ArrayList(supportedCiphers.size()); + newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size()); } for (String c : ciphers) { if (c == null) { diff --git a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java index cda1419a97..c4563a99dd 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java @@ -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 list = new ArrayList(); + List 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 list = new ArrayList(); + List list = InternalThreadLocalMap.get().arrayList(); for (String f: fingerprints) { if (f == null) { break;