2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
17
package org.apache.solr.search;
19
import org.apache.lucene.util.LuceneTestCase;
20
import org.apache.solr.common.util.NamedList;
21
import org.apache.solr.util.ConcurrentLRUCache;
23
import java.io.IOException;
24
import java.util.HashMap;
26
import java.util.Random;
27
import java.util.concurrent.atomic.AtomicInteger;
31
* Test for FastLRUCache
33
* @version $Id: TestFastLRUCache.java 1170772 2011-09-14 19:09:56Z sarowe $
34
* @see org.apache.solr.search.FastLRUCache
37
public class TestFastLRUCache extends LuceneTestCase {
38
public void testSimple() throws IOException {
39
FastLRUCache sc = new FastLRUCache();
40
Map l = new HashMap();
42
l.put("initialSize", "10");
43
l.put("autowarmCount", "25");
44
CacheRegenerator cr = new CacheRegenerator() {
45
public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache,
46
SolrCache oldCache, Object oldKey, Object oldVal) throws IOException {
47
newCache.put(oldKey, oldVal);
51
Object o = sc.init(l, null, cr);
52
sc.setState(SolrCache.State.LIVE);
53
for (int i = 0; i < 101; i++) {
54
sc.put(i + 1, "" + (i + 1));
56
assertEquals("25", sc.get(25));
57
assertEquals(null, sc.get(110));
58
NamedList nl = sc.getStatistics();
59
assertEquals(2L, nl.get("lookups"));
60
assertEquals(1L, nl.get("hits"));
61
assertEquals(101L, nl.get("inserts"));
63
assertEquals(null, sc.get(1)); // first item put in should be the first out
66
FastLRUCache scNew = new FastLRUCache();
69
scNew.setState(SolrCache.State.LIVE);
71
scNew.put(103, "103");
72
assertEquals("90", scNew.get(90));
73
assertEquals(null, scNew.get(50));
74
nl = scNew.getStatistics();
75
assertEquals(2L, nl.get("lookups"));
76
assertEquals(1L, nl.get("hits"));
77
assertEquals(1L, nl.get("inserts"));
78
assertEquals(0L, nl.get("evictions"));
80
assertEquals(5L, nl.get("cumulative_lookups"));
81
assertEquals(2L, nl.get("cumulative_hits"));
82
assertEquals(102L, nl.get("cumulative_inserts"));
86
public void testOldestItems() {
87
ConcurrentLRUCache<Integer, String> cache = new ConcurrentLRUCache<Integer, String>(100, 90);
88
for (int i = 0; i < 50; i++) {
89
cache.put(i + 1, "" + (i + 1));
93
Map<Integer, String> m = cache.getOldestAccessedItems(5);
95
assertNotNull(m.get(7));
96
assertNotNull(m.get(6));
97
assertNotNull(m.get(5));
98
assertNotNull(m.get(4));
99
assertNotNull(m.get(2));
101
m = cache.getOldestAccessedItems(0);
102
assertTrue(m.isEmpty());
105
m = cache.getLatestAccessedItems(0);
106
assertTrue(m.isEmpty());
111
void doPerfTest(int iter, int cacheSize, int maxKey) {
112
long start = System.currentTimeMillis();
114
int lowerWaterMark = cacheSize;
115
int upperWaterMark = (int)(lowerWaterMark * 1.1);
118
ConcurrentLRUCache cache = new ConcurrentLRUCache(upperWaterMark, lowerWaterMark, (upperWaterMark+lowerWaterMark)/2, upperWaterMark, false, false, null);
119
boolean getSize=false;
120
int minSize=0,maxSize=0;
121
for (int i=0; i<iter; i++) {
122
cache.put(r.nextInt(maxKey),"TheValue");
123
int sz = cache.size();
124
if (!getSize && sz >= cacheSize) {
128
if (sz < minSize) minSize=sz;
129
else if (sz > maxSize) maxSize=sz;
134
long end = System.currentTimeMillis();
135
System.out.println("time=" + (end-start) + ", minSize="+minSize+",maxSize="+maxSize);
139
public void testPerf() {
140
doPerfTest(1000000, 100000, 200000); // big cache, warmup
141
doPerfTest(2000000, 100000, 200000); // big cache
142
doPerfTest(2000000, 100000, 120000); // smaller key space increases distance between oldest, newest and makes the first passes less effective.
143
doPerfTest(6000000, 1000, 2000); // small cache, smaller hit rate
144
doPerfTest(6000000, 1000, 1200); // small cache, bigger hit rate
148
// returns number of puts
149
int useCache(SolrCache sc, int numGets, int maxKey, int seed) {
151
Random r = new Random(seed);
153
// use like a cache... gets and a put if not found
154
for (int i=0; i<numGets; i++) {
155
Integer k = r.nextInt(maxKey);
156
Integer v = (Integer)sc.get(k);
166
void fillCache(SolrCache sc, int cacheSize, int maxKey) {
167
for (int i=0; i<cacheSize; i++) {
168
Integer kv = random.nextInt(maxKey);
174
void cachePerfTest(final SolrCache sc, final int nThreads, final int numGets, int cacheSize, final int maxKey) {
175
Map l = new HashMap();
176
l.put("size", ""+cacheSize);
177
l.put("initialSize", ""+cacheSize);
179
Object o = sc.init(l, null, null);
180
sc.setState(SolrCache.State.LIVE);
182
fillCache(sc, cacheSize, maxKey);
184
long start = System.currentTimeMillis();
186
Thread[] threads = new Thread[nThreads];
187
final AtomicInteger puts = new AtomicInteger(0);
188
for (int i=0; i<threads.length; i++) {
189
final int seed=random.nextInt();
190
threads[i] = new Thread() {
193
int ret = useCache(sc, numGets/nThreads, maxKey, seed);
199
for (Thread thread : threads) {
202
} catch (Exception e) {
207
for (Thread thread : threads) {
210
} catch (Exception e) {
215
long end = System.currentTimeMillis();
216
System.out.println("time=" + (end-start) + " impl=" +sc.getClass().getSimpleName()
217
+" nThreads= " + nThreads + " size="+cacheSize+" maxKey="+maxKey+" gets="+numGets
218
+" hitRatio="+(1-(((double)puts.get())/numGets)));
221
void perfTestBoth(int nThreads, int numGets, int cacheSize, int maxKey) {
222
cachePerfTest(new LRUCache(), nThreads, numGets, cacheSize, maxKey);
223
cachePerfTest(new FastLRUCache(), nThreads, numGets, cacheSize, maxKey);
227
public void testCachePerf() {
229
perfTestBoth(2, 100000, 100000, 120000);
230
perfTestBoth(1, 2000000, 100000, 100000); // big cache, 100% hit ratio
231
perfTestBoth(2, 2000000, 100000, 100000); // big cache, 100% hit ratio
232
perfTestBoth(1, 2000000, 100000, 120000); // big cache, bigger hit ratio
233
perfTestBoth(2, 2000000, 100000, 120000); // big cache, bigger hit ratio
234
perfTestBoth(1, 2000000, 100000, 200000); // big cache, ~50% hit ratio
235
perfTestBoth(2, 2000000, 100000, 200000); // big cache, ~50% hit ratio
236
perfTestBoth(1, 2000000, 100000, 1000000); // big cache, ~10% hit ratio
237
perfTestBoth(2, 2000000, 100000, 1000000); // big cache, ~10% hit ratio
239
perfTestBoth(1, 2000000, 1000, 1000); // small cache, ~100% hit ratio
240
perfTestBoth(2, 2000000, 1000, 1000); // small cache, ~100% hit ratio
241
perfTestBoth(1, 2000000, 1000, 1200); // small cache, bigger hit ratio
242
perfTestBoth(2, 2000000, 1000, 1200); // small cache, bigger hit ratio
243
perfTestBoth(1, 2000000, 1000, 2000); // small cache, ~50% hit ratio
244
perfTestBoth(2, 2000000, 1000, 2000); // small cache, ~50% hit ratio
245
perfTestBoth(1, 2000000, 1000, 10000); // small cache, ~10% hit ratio
246
perfTestBoth(2, 2000000, 1000, 10000); // small cache, ~10% hit ratio