Dynamic Proxy Classes를 활용한 Java Memoization
아래의 예제들은 객체의 함수 프로세스를 캐싱을 통해 성능 향상을 가져다 주는 케이스입니다.
1. 클래스 Memoizer
성능에 차이가 나죠? ^^
4. Memoizer 클래스 활용 가능한 경우
[참조 사이트]
1. 클래스 Memoizer
import java.lang.reflect.InvocationHandler;2. 테스트 클래스 PerformanceTest
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Memoizer implements InvocationHandler {
private Object object;
private Map caches = new HashMap();
private Memoizer(Object object) {
this.object = object;
}
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getReturnType().equals(Void.TYPE)) {
// Don't cache void methods
return invoke(method, args);
} else {
Map cache = this.getCache(method);
List key = Arrays.asList(args);
Object value = cache.get(key);
if (value == null && !cache.containsKey(key)) {
value = invoke(method, args);
cache.put(key, value);
}
return value;
}
}
private Object invoke(Method method, Object[] args)
throws Throwable {
try {
return method.invoke(object, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
public static Object memoize(Object object) {
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new Memoizer(object));
}
private synchronized Map getCache(Method m) {
Map cache = (Map) caches.get(m);
if (cache == null) {
cache = Collections.synchronizedMap(new HashMap());
caches.put(m, cache);
}
return cache;
}
}
public class PerformanceTest3. 성능 테스트 결과
{
public PerformanceTest(){}
private long time(final BinaryDigitsCalculator calculator,
final int n, final int loops) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < loops; i++) {
calculator.calculateBinaryDigit(n);
}
return System.currentTimeMillis() - startTime;
}
private void check(final int n, final int loops) {
BinaryDigitsCalculator memoizedCalculator =
(BinaryDigitsCalculator) Memoizer.memoize(
new PiBinaryDigitsCalculator());
long millis = time(memoizedCalculator, n, loops);
System.out.println("Memoized. n = " + n +
", loops = " + loops + ": " + millis + "ms");
BinaryDigitsCalculator calculator =
new PiBinaryDigitsCalculator();
millis = time(calculator, n, loops);
System.out.println("Not memoized. n = " + n +
", loops = " + loops + ": " + millis + "ms");
}
public void testSmallN() {
check(-1, 1000);
}
public void testLargeN() {
check(-1000, 1000);
}
public static void main(String[] args) {
PerformanceTest pt = null;
try {
pt = new PerformanceTest();
pt.testSmallN();
pt.testLargeN();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Memoized. n = -1, loops = 1000: 0ms
Not memoized. n = -1, loops = 1000: 15ms
Memoized. n = -1000, loops = 1000: 16ms
Not memoized. n = -1000, loops = 1000: 1625ms
성능에 차이가 나죠? ^^
4. Memoizer 클래스 활용 가능한 경우
- 함수 호출에 대해 해당 함수의 리턴 값이 불변해야
- 함수에 side effects가 없어야
- 함수 인자가 mutable 이 아니어야
[참조 사이트]








