Unverified 提交 07e4ef94 authored 作者: Andrei Tokar's avatar Andrei Tokar 提交者: GitHub

Merge pull request #1310 from h2database/lisr-weak

Use weak references in LIRS cache
package org.h2.test.db;
import org.h2.mvstore.cache.CacheLongKeyLIRS;
import org.h2.test.TestBase;
import org.h2.test.TestDb;
import java.util.Random;
/**
* Class TestLIRSMemoryConsumption.
* <UL>
* <LI> 8/5/18 10:57 PM initial creation
* </UL>
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
public class TestLIRSMemoryConsumption extends TestDb {
/**
* Run just this test.
*
* @param a
* ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() {
testMemoryConsumption();
System.out.println("-----------------------");
testMemoryConsumption();
System.out.println("-----------------------");
testMemoryConsumption();
}
private void testMemoryConsumption() {
int size = 1_000_000;
Random rng = new Random();
CacheLongKeyLIRS.Config config = new CacheLongKeyLIRS.Config();
for (int mb = 1; mb <= 16; mb *= 2) {
config.maxMemory = mb * 1024 * 1024;
CacheLongKeyLIRS<Object> cache = new CacheLongKeyLIRS<>(config);
long memoryUsedInitial = getMemUsedKb();
for (int i = 0; i < size; i++) {
cache.put(i, createValue(i), getValueSize(i));
}
for (int i = 0; i < size; i++) {
int key;
int mode = rng.nextInt(4);
switch(mode) {
default:
case 0:
key = rng.nextInt(10);
break;
case 1:
key = rng.nextInt(100);
break;
case 2:
key = rng.nextInt(10_000);
break;
case 3:
key = rng.nextInt(1_000_000);
break;
}
Object val = cache.get(key);
if (val == null) {
cache.put(key, createValue(key), getValueSize(key));
}
}
eatMemory(1);
freeMemory();
cache.trimNonResidentQueue();
long memoryUsed = getMemUsedKb();
int sizeHot = cache.sizeHot();
int sizeResident = cache.size();
int sizeNonResident = cache.sizeNonResident();
long hits = cache.getHits();
long misses = cache.getMisses();
System.out.println(mb + " | " +
(memoryUsed - memoryUsedInitial + 512) / 1024 + " | " +
(sizeResident+sizeNonResident) + " | " +
sizeHot + " | " + (sizeResident - sizeHot) + " | " + sizeNonResident +
" | " + (hits * 100 / (hits + misses)) );
}
}
private Object createValue(long key) {
// return new Object();
return new byte[2540];
}
private int getValueSize(long key) {
// return 16;
return 2560;
}
private long getMemUsedKb() {
Runtime rt = Runtime.getRuntime();
long memory = Long.MAX_VALUE;
for (int i = 0; i < 8; i++) {
rt.gc();
long memNow = (rt.totalMemory() - rt.freeMemory()) / 1024;
if (memNow >= memory) {
break;
}
memory = memNow;
try { Thread.sleep(1000); } catch (InterruptedException e) {/**/}
}
return memory;
}
}
...@@ -46,28 +46,37 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -46,28 +46,37 @@ public class TestCacheLongKeyLIRS extends TestBase {
testRandomOperations(); testRandomOperations();
} }
private static void testRandomSmallCache() { private void testRandomSmallCache() {
Random r = new Random(1); Random r = new Random(1);
for (int i = 0; i < 10000; i++) { for (int i = 0; i < 10000; i++) {
int j = 0; int j = 0;
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
CacheLongKeyLIRS<Integer> test = createCache(1 + r.nextInt(10)); int maxSize = 1 + r.nextInt(10);
buff.append("size:").append(maxSize).append('\n');
CacheLongKeyLIRS<Integer> test = createCache(maxSize);
for (; j < 30; j++) { for (; j < 30; j++) {
int key = r.nextInt(5); String lastState = toString(test);
switch (r.nextInt(3)) { try {
case 0: int key = r.nextInt(5);
int memory = r.nextInt(5) + 1; switch (r.nextInt(3)) {
buff.append("add ").append(key).append(' '). case 0:
append(memory).append('\n'); int memory = r.nextInt(5) + 1;
test.put(key, j, memory); buff.append("add ").append(key).append(' ').
break; append(memory).append('\n');
case 1: test.put(key, j, memory);
buff.append("remove ").append(key).append('\n'); break;
test.remove(key); case 1:
break; buff.append("remove ").append(key).append('\n');
case 2: test.remove(key);
buff.append("get ").append(key).append('\n'); break;
test.get(key); case 2:
buff.append("get ").append(key).append('\n');
test.get(key);
}
verify(test, null);
} catch (Throwable ex) {
println(i + "\n" + buff + "\n" + lastState + "\n" + toString(test));
throw ex;
} }
} }
} }
...@@ -108,8 +117,8 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -108,8 +117,8 @@ public class TestCacheLongKeyLIRS extends TestBase {
test.put(j, j); test.put(j, j);
} }
// for a cache of size 1000, // for a cache of size 1000,
// there are 63 cold entries (about 6.25%). // there are 32 cold entries (about 1/32).
assertEquals(63, test.size() - test.sizeHot()); assertEquals(32, test.size() - test.sizeHot());
// at most as many non-resident elements // at most as many non-resident elements
// as there are entries in the stack // as there are entries in the stack
assertEquals(1000, test.size()); assertEquals(1000, test.size());
...@@ -164,22 +173,22 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -164,22 +173,22 @@ public class TestCacheLongKeyLIRS extends TestBase {
assertEquals(1, test.getMemory(5)); assertEquals(1, test.getMemory(5));
assertEquals(0, test.getMemory(4)); assertEquals(0, test.getMemory(4));
assertEquals(0, test.getMemory(100)); assertEquals(0, test.getMemory(100));
assertNull(test.peek(4)); assertNotNull(test.peek(4));
assertNull(test.get(4)); assertNotNull(test.get(4));
assertEquals(10, test.get(1).intValue()); assertEquals(10, test.get(1).intValue());
assertEquals(20, test.get(2).intValue()); assertEquals(20, test.get(2).intValue());
assertEquals(30, test.get(3).intValue()); assertEquals(30, test.get(3).intValue());
verify(test, "mem: 4 stack: 3 2 1 cold: 5 non-resident: 4"); verify(test, "mem: 5 stack: 3 2 1 cold: 4 5 non-resident:");
assertEquals(50, test.get(5).intValue()); assertEquals(50, test.get(5).intValue());
verify(test, "mem: 4 stack: 5 3 2 1 cold: 5 non-resident: 4"); verify(test, "mem: 5 stack: 5 3 2 1 cold: 5 4 non-resident:");
assertEquals(50, test.get(5).intValue()); assertEquals(50, test.get(5).intValue());
verify(test, "mem: 4 stack: 5 3 2 cold: 1 non-resident: 4"); verify(test, "mem: 5 stack: 5 3 2 cold: 1 4 non-resident:");
// remove // remove
assertEquals(50, test.remove(5).intValue()); assertEquals(50, test.remove(5).intValue());
assertNull(test.remove(5)); assertNull(test.remove(5));
verify(test, "mem: 3 stack: 3 2 1 cold: non-resident: 4"); verify(test, "mem: 4 stack: 3 2 1 cold: 4 non-resident:");
assertNull(test.remove(4)); assertNotNull(test.remove(4));
verify(test, "mem: 3 stack: 3 2 1 cold: non-resident:"); verify(test, "mem: 3 stack: 3 2 1 cold: non-resident:");
assertNull(test.remove(4)); assertNull(test.remove(4));
verify(test, "mem: 3 stack: 3 2 1 cold: non-resident:"); verify(test, "mem: 3 stack: 3 2 1 cold: non-resident:");
...@@ -195,7 +204,7 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -195,7 +204,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
verify(test, "mem: 3 stack: 4 3 2 cold: non-resident: 1"); verify(test, "mem: 3 stack: 4 3 2 cold: non-resident: 1");
assertEquals(20, test.remove(2).intValue()); assertEquals(20, test.remove(2).intValue());
assertFalse(test.containsKey(1)); assertFalse(test.containsKey(1));
assertNull(test.remove(1)); assertEquals(10, test.remove(1).intValue());
assertFalse(test.containsKey(1)); assertFalse(test.containsKey(1));
verify(test, "mem: 2 stack: 4 3 cold: non-resident:"); verify(test, "mem: 2 stack: 4 3 cold: non-resident:");
test.put(1, 10); test.put(1, 10);
...@@ -226,7 +235,7 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -226,7 +235,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
// 1 was non-resident, so this should make it hot // 1 was non-resident, so this should make it hot
test.put(1, 10); test.put(1, 10);
verify(test, "mem: 4 stack: 1 5 4 3 cold: 2 non-resident: 5"); verify(test, "mem: 4 stack: 1 5 4 3 cold: 2 non-resident: 5");
assertFalse(test.containsValue(50)); assertTrue(test.containsValue(50));
test.remove(2); test.remove(2);
test.remove(3); test.remove(3);
test.remove(4); test.remove(4);
...@@ -324,7 +333,7 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -324,7 +333,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
} }
assertEquals(100, test.size()); assertEquals(100, test.size());
assertEquals(200, test.sizeNonResident()); assertEquals(200, test.sizeNonResident());
assertEquals(90, test.sizeHot()); assertEquals(96, test.sizeHot());
} }
private void testLimitNonResident() { private void testLimitNonResident() {
...@@ -332,8 +341,8 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -332,8 +341,8 @@ public class TestCacheLongKeyLIRS extends TestBase {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
test.put(i, 10 * i); test.put(i, 10 * i);
} }
verify(test, "mem: 4 stack: 19 18 17 16 15 14 13 12 11 10 3 2 1 " + verify(test, "mem: 4 stack: 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 " +
"cold: 19 non-resident: 18 17 16 15 14 13 12 11 10"); "cold: 19 non-resident: 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 0");
} }
private void testLimitMemory() { private void testLimitMemory() {
...@@ -344,10 +353,10 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -344,10 +353,10 @@ public class TestCacheLongKeyLIRS extends TestBase {
verify(test, "mem: 4 stack: 4 3 2 1 cold: 4 non-resident: 0"); verify(test, "mem: 4 stack: 4 3 2 1 cold: 4 non-resident: 0");
assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4); assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4);
test.put(6, 60, 3); test.put(6, 60, 3);
verify(test, "mem: 4 stack: 6 4 3 cold: 6 non-resident: 2 1 4"); verify(test, "mem: 4 stack: 6 4 3 cold: 6 non-resident: 2 1 4 0");
assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4); assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4);
test.put(7, 70, 3); test.put(7, 70, 3);
verify(test, "mem: 4 stack: 7 6 3 cold: 7 non-resident: 6 2 1"); verify(test, "mem: 4 stack: 7 6 4 3 cold: 7 non-resident: 6 2 1 4 0");
assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4); assertTrue("" + test.getUsedMemory(), test.getUsedMemory() <= 4);
test.put(8, 80, 4); test.put(8, 80, 4);
verify(test, "mem: 4 stack: 8 cold: non-resident:"); verify(test, "mem: 4 stack: 8 cold: non-resident:");
...@@ -369,7 +378,7 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -369,7 +378,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
test.put(i, i * 10); test.put(i, i * 10);
test.get(i); test.get(i);
if (log) { if (log) {
System.out.println("get " + i + " -> " + test); println("get " + i + " -> " + test);
} }
} }
verify(test, null); verify(test, null);
...@@ -394,14 +403,13 @@ public class TestCacheLongKeyLIRS extends TestBase { ...@@ -394,14 +403,13 @@ public class TestCacheLongKeyLIRS extends TestBase {
} }
verify(test, null); verify(test, null);
} }
// ensure 0..9 are hot, 10..17 are not resident, 18..19 are cold // ensure 0..9 are hot, 10..17 are not resident, 18..19 are cold
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Integer x = test.get(i); Integer x = test.get(i);
if (i < size / 2 || i == size - 1 || i == size - 2) { if (i < size / 2 || i == size - 1 || i == size - 2) {
assertNotNull("i: " + i, x); assertNotNull("i: " + i, x);
assertEquals(i * 10, x.intValue()); assertEquals(i * 10, x.intValue());
} else {
assertNull(x);
} }
verify(test, null); verify(test, null);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论