Skip to content

Commit c736460

Browse files
committed
Merge pull request alibaba#222 from ttoommbb/master
Optimize ThreadLocalCache while perusal the code.
2 parents e9dba1d + 9bf7994 commit c736460

File tree

2 files changed

+111
-20
lines changed

2 files changed

+111
-20
lines changed

src/main/java/com/alibaba/fastjson/util/ThreadLocalCache.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
public class ThreadLocalCache {
77

8-
public final static int CHARS_CACH_INIT_SIZE = 1024; // 1k;
9-
public final static int CHARS_CACH_MAX_SIZE = 1024 * 128; // 1k;
8+
public final static int CHARS_CACH_INIT_SIZE = 1024; // 1k, 2^10;
9+
public final static int CHARS_CACH_INIT_SIZE_EXP = 10;
10+
public final static int CHARS_CACH_MAX_SIZE = 1024 * 128; // 128k, 2^17;
11+
public final static int CHARS_CACH_MAX_SIZE_EXP = 17;
1012
private final static ThreadLocal<SoftReference<char[]>> charsBufLocal = new ThreadLocal<SoftReference<char[]>>();
1113

1214
private final static ThreadLocal<CharsetDecoder> decoderLocal = new ThreadLocal<CharsetDecoder>();
@@ -45,17 +47,30 @@ public static char[] getChars(int length) {
4547
}
4648

4749
private static char[] allocate(int length) {
48-
int allocateLength = getAllocateLength(CHARS_CACH_INIT_SIZE, CHARS_CACH_MAX_SIZE, length);
50+
if(length> CHARS_CACH_MAX_SIZE) {
51+
return new char[length];
52+
}
4953

50-
if (allocateLength <= CHARS_CACH_MAX_SIZE) {
51-
char[] chars = new char[allocateLength];
52-
charsBufLocal.set(new SoftReference<char[]>(chars));
53-
return chars;
54-
}
55-
56-
return new char[length];
54+
int allocateLength = getAllocateLengthExp(CHARS_CACH_INIT_SIZE_EXP, CHARS_CACH_MAX_SIZE_EXP, length);
55+
char[] chars = new char[allocateLength];
56+
charsBufLocal.set(new SoftReference<char[]>(chars));
57+
return chars;
5758
}
5859

60+
private static int getAllocateLengthExp(int minExp, int maxExp, int length) {
61+
assert maxExp >= length;
62+
// int max = 1 << maxExp;
63+
// if(length>= max) {
64+
// return length;
65+
// }
66+
int part = length >>> minExp;
67+
if(part <= 0) {
68+
return 1<< minExp;
69+
}
70+
return 1 << 32 - Integer.numberOfLeadingZeros(length-1);
71+
}
72+
73+
@Deprecated
5974
private static int getAllocateLength(int init, int max, int length) {
6075
int value = init;
6176
for (;;) {
@@ -74,8 +89,10 @@ private static int getAllocateLength(int init, int max, int length) {
7489
}
7590

7691
// /////////
77-
public final static int BYTES_CACH_INIT_SIZE = 1024; // 1k;
78-
public final static int BYTeS_CACH_MAX_SIZE = 1024 * 128; // 1k;
92+
public final static int BYTES_CACH_INIT_SIZE = 1024; // 1k, 2^10;
93+
public final static int BYTES_CACH_INIT_SIZE_EXP = 10;
94+
public final static int BYTES_CACH_MAX_SIZE = 1024 * 128; // 128k, 2^17;
95+
public final static int BYTES_CACH_MAX_SIZE_EXP = 17;
7996
private final static ThreadLocal<SoftReference<byte[]>> bytesBufLocal = new ThreadLocal<SoftReference<byte[]>>();
8097

8198
public static void clearBytes() {
@@ -103,15 +120,14 @@ public static byte[] getBytes(int length) {
103120
}
104121

105122
private static byte[] allocateBytes(int length) {
106-
int allocateLength = getAllocateLength(CHARS_CACH_INIT_SIZE, CHARS_CACH_MAX_SIZE, length);
123+
if(length > CHARS_CACH_MAX_SIZE) {
124+
return new byte[length];
125+
}
107126

108-
if (allocateLength <= CHARS_CACH_MAX_SIZE) {
109-
byte[] chars = new byte[allocateLength];
110-
bytesBufLocal.set(new SoftReference<byte[]>(chars));
111-
return chars;
112-
}
113-
114-
return new byte[length];
127+
int allocateLength = getAllocateLengthExp(CHARS_CACH_INIT_SIZE_EXP, CHARS_CACH_MAX_SIZE_EXP, length);
128+
byte[] chars = new byte[allocateLength];
129+
bytesBufLocal.set(new SoftReference<byte[]>(chars));
130+
return chars;
115131
}
116132

117133
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.alibaba.json.bvt.util;
2+
3+
import java.lang.reflect.Method;
4+
import java.util.Random;
5+
import java.util.concurrent.ThreadLocalRandom;
6+
7+
import org.junit.Assert;
8+
import org.junit.BeforeClass;
9+
import org.junit.Test;
10+
11+
import com.alibaba.fastjson.util.ThreadLocalCache;
12+
13+
import static com.alibaba.fastjson.util.ThreadLocalCache.CHARS_CACH_INIT_SIZE;
14+
import static com.alibaba.fastjson.util.ThreadLocalCache.CHARS_CACH_INIT_SIZE_EXP;
15+
import static com.alibaba.fastjson.util.ThreadLocalCache.CHARS_CACH_MAX_SIZE;
16+
import static com.alibaba.fastjson.util.ThreadLocalCache.CHARS_CACH_MAX_SIZE_EXP;
17+
18+
/**
19+
* Test new {@link #getAllocateLengthExp(int, int, int)} comparing with old
20+
* {@link #getAllocateLength(int, int, int)} method in {@link ThreadLocalCache}.
21+
*/
22+
public class TheradLocalCacheUnitTest {
23+
24+
static Method getAllocateLengthMethod;
25+
static Method getAllocateLengthExpMethod;
26+
27+
@BeforeClass
28+
public static void init() throws Exception {
29+
getAllocateLengthMethod = ThreadLocalCache.class.getDeclaredMethod("getAllocateLength",
30+
int.class, int.class, int.class);
31+
getAllocateLengthExpMethod = ThreadLocalCache.class.getDeclaredMethod(
32+
"getAllocateLengthExp", int.class, int.class, int.class);
33+
34+
getAllocateLengthMethod.setAccessible(true);
35+
getAllocateLengthExpMethod.setAccessible(true);
36+
}
37+
38+
@Test
39+
public void getAllocateLengthNewImplementationRandomTest() throws Exception {
40+
Random random = ThreadLocalRandom.current();
41+
for (int i = 0; i < 100000000; i++) {
42+
int length = Math.abs(random.nextInt());
43+
testSample(length);
44+
}
45+
}
46+
47+
@Test
48+
public void getAllocateLengthNewImplementationCertainTest() throws Exception {
49+
testSample(0);
50+
testSample(1);
51+
testSample(1024);
52+
testSample(CHARS_CACH_INIT_SIZE);
53+
testSample(CHARS_CACH_MAX_SIZE);
54+
testSample(114649);
55+
}
56+
57+
private void testSample(int length) throws Exception {
58+
if (length > CHARS_CACH_MAX_SIZE) {
59+
return;
60+
}
61+
int oldWay = getAllocateLength(CHARS_CACH_INIT_SIZE, CHARS_CACH_MAX_SIZE, length);
62+
int newWay = getAllocateLengthExp(CHARS_CACH_INIT_SIZE_EXP, CHARS_CACH_MAX_SIZE_EXP, length);
63+
// since new method always need premise length <= max value. we need to
64+
// skip this case.
65+
Assert.assertEquals("error when test length:" + length, oldWay, newWay);
66+
}
67+
68+
private static int getAllocateLength(int init, int max, int length) throws Exception {
69+
return (Integer) getAllocateLengthMethod.invoke(null, init, max, length);
70+
}
71+
72+
private static int getAllocateLengthExp(int minExp, int maxExp, int length) throws Exception {
73+
return (Integer) getAllocateLengthExpMethod.invoke(null, minExp, maxExp, length);
74+
}
75+
}

0 commit comments

Comments
 (0)