Skip to content

Commit

Permalink
Add a reusable ArrayList to InternalThreadLocalMap
Browse files Browse the repository at this point in the history
Motivation:

See netty#3411. A reusable ArrayList in InternalThreadLocalMap can avoid allocations in the following pattern:

```
List<...> list = new ArrayList<...>();

add something to list but never use InternalThreadLocalMap

return list.toArray(new ...[list.size()]);

```

Modifications:

Add a reusable ArrayList to InternalThreadLocalMap and update codes to use it.

Result:

Reuse a thread local ArrayList to avoid allocations.
  • Loading branch information
windie authored and normanmaurer committed Feb 1, 2016
1 parent b354868 commit b7415a3
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
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;
Expand Down Expand Up @@ -196,7 +196,7 @@ public String encode(Iterable<? extends Cookie> cookies) {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.StringUtil;

import java.io.IOException;
Expand Down Expand Up @@ -1836,7 +1837,7 @@ private static String[] splitMultipartHeader(String sb) {
* @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;
Expand Down
4 changes: 2 additions & 2 deletions common/src/main/java/io/netty/util/AsciiString.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

import io.netty.util.ByteProcessor.IndexOfProcessor;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.PlatformDependent;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -1050,7 +1050,7 @@ public AsciiString[] split(String expr, int max) {
* Splits the specified {@link String} with the specified delimiter..
*/
public AsciiString[] split(char delim) {
final List<AsciiString> res = new ArrayList<AsciiString>();
final List<AsciiString> res = InternalThreadLocalMap.get().arrayList();

int start = 0;
final int length = length();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand All @@ -34,6 +35,8 @@
*/
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() {
Expand Down Expand Up @@ -160,6 +163,9 @@ public int size() {
if (charsetDecoderCache != null) {
count ++;
}
if (arrayList != null) {
count ++;
}

for (Object o: indexedVariables) {
if (o != UNSET) {
Expand Down Expand Up @@ -198,6 +204,21 @@ public Map<Charset, CharsetDecoder> charsetDecoderCache() {
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;
}
Expand Down
5 changes: 2 additions & 3 deletions common/src/main/java/io/netty/util/internal/StringUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -94,7 +93,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 ++) {
Expand Down Expand Up @@ -136,7 +135,7 @@ public static String[] split(String value, char delim) {
*/
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;

Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -33,7 +34,7 @@ public String[] filterCipherSuites(Iterable<String> ciphers, List<String> defaul
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;
Expand Down
4 changes: 2 additions & 2 deletions handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand All @@ -42,7 +43,6 @@
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;
Expand Down Expand Up @@ -1018,7 +1018,7 @@ public String[] getSupportedProtocols() {

@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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,10 +41,10 @@ public String[] filterCipherSuites(Iterable<String> ciphers, List<String> defaul

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand All @@ -31,7 +32,6 @@
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;
Expand Down Expand Up @@ -151,7 +151,7 @@ public FingerprintTrustManagerFactory(byte[]... fingerprints) {
throw new NullPointerException("fingerprints");
}

List<byte[]> list = new ArrayList<byte[]>();
List<byte[]> list = InternalThreadLocalMap.get().arrayList();
for (byte[] f: fingerprints) {
if (f == null) {
break;
Expand All @@ -171,7 +171,7 @@ private static byte[][] toFingerprintArray(Iterable<String> fingerprints) {
throw new NullPointerException("fingerprints");
}

List<byte[]> list = new ArrayList<byte[]>();
List<byte[]> list = InternalThreadLocalMap.get().arrayList();
for (String f: fingerprints) {
if (f == null) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.resolver.HostsFileEntriesResolver;
import io.netty.util.internal.InternalThreadLocalMap;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkNotNull;
Expand Down Expand Up @@ -170,7 +170,7 @@ public DnsNameResolverBuilder resolvedAddressTypes(InternetProtocolFamily... res
checkNotNull(resolvedAddressTypes, "resolvedAddressTypes");

final List<InternetProtocolFamily> list =
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
InternalThreadLocalMap.get().arrayList(InternetProtocolFamily.values().length);

for (InternetProtocolFamily f : resolvedAddressTypes) {
if (f == null) {
Expand Down Expand Up @@ -207,7 +207,7 @@ public DnsNameResolverBuilder resolvedAddressTypes(Iterable<InternetProtocolFami
checkNotNull(resolvedAddressTypes, "resolveAddressTypes");

final List<InternetProtocolFamily> list =
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
InternalThreadLocalMap.get().arrayList(InternetProtocolFamily.values().length);

for (InternetProtocolFamily f : resolvedAddressTypes) {
if (f == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.netty.resolver.dns;

import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

Expand Down Expand Up @@ -241,7 +242,7 @@ private static InetSocketAddress[] sanitize(InetSocketAddress[] addresses) {
throw new NullPointerException("addresses");
}

List<InetSocketAddress> list = new ArrayList<InetSocketAddress>(addresses.length);
List<InetSocketAddress> list = InternalThreadLocalMap.get().arrayList(addresses.length);
for (InetSocketAddress a: addresses) {
if (a == null) {
break;
Expand Down

0 comments on commit b7415a3

Please sign in to comment.