<< Business Model Evalutaion | | 마이클 잭슨 사망 >>

Dynamic Proxy Classes를 활용한 Java Memoization

아래의 예제들은 객체의 함수 프로세스를 캐싱을 통해 성능 향상을 가져다 주는 케이스입니다.

1.  클래스 Memoizer

import java.lang.reflect.InvocationHandler;
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;
}
}
2. 테스트 클래스 PerformanceTest

public class PerformanceTest
{
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();
}
}
}
3. 성능 테스트 결과

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 이 아니어야

[참조 사이트]
태그 :



코멘트 달기 Send a TrackBack